ADDITIONAL SYSTEM INFORMATION 


Version 2.10 


February 3, 1995 


(C) Copyright Psion PLC 1990-95 


All rights reserved. This manual and the programs referred to herein are copyrighted works of Psion 
PLC, London, England. Reproduction in whole or in part, including utilization in machines capable of 
reproduction or retrieval, without express written permission of Psion PLC, is prohibited. Reverse 
engineering is also prohibited. 


The information in this document is subject to change without notice. 


Psion and the Psion logo are registered trademarks, and Psion, Psion MC, Psion HC, Psion Series 3, 
Psion Series 3a and Psion Workabout are trademarks of Psion PLC. 


TopSpeed is a registered trademark of Clarion Software Corporation. IBM, IBM XT and IBM AT are 
registered trademarks of International Business Machines Corp. Microsoft and MS-DOS are registered 
trademarks of Microsoft Corporation. Apple and Macintosh are registered trademarks of Apple Computer 
Inc. VAX and VMS are registered trademarks of Digital Equipment Corporation. Brief is a registered 
trademark of Underware Inc. Psion PLC acknowledges that some other names referred to are registered 
trademarks. 


Contents 
1. Melink, (Moeprint:and Sink 2. :00..<550..hccsie belive kiileseccoccecccceetasabecsaseteasseccecitverss 1 
MIGIINK OX6 iis fe Serie oles tieaencoweaereensdovesasee cuettes Sea eete deemed oma cea eee ee Tee vers 1 
Commands provided in MCLINK............:sscsecscsesccscscecenscceceveceseseentceceses 1 
MCLINK and single floppy disk drive PCS ..............cccsccececcuscecscscuceeesens 2 
Exiting the MCLINK Program .......csscscscsscsscccecscsececnsssesccerscuteccssonesencees 2 
Display the version Of MCLINK ..............ccceccsesenercoscecetecanseeceaevsesacenense 2 
MCLINK file-handling COMMAMAS ..............cecrerecsececscsesecsccecsseesscecscscvesvevecs 2 
Rules: On TEMaMES iid occce tes cecdacsecnceadecceackeeet neat he ettees aut eeia 2 
DIR feos cnc cedvevsevesvacese dete ci seaccade ide tacisesnde hstereee ete ee Bac sa ees ee eens 3 
CON sc owctsciecevesictaen ste ceeds cove cu dvccedh ceca teas scale ccc Bh ce eee oe eed eens 3 
RENAME cite crave vesta odes chon te sttecaccedcusstea cee ceeattvet tu ovteareie tee aaes wives mceeied ale 4 
DEC ETE cc ctaccescccettsntcccit ee hcteteatoctea ts meere tee Merete te satin Marta eer oriaee 4 
IMIR DIR iirc cette eteeearccccccc tere fed eens cae Ee ne Pa ee ee a 4, 
Changing MCLINK communications SettingS.............:eseccsscesssscseececeucsesessees 4 
Options for the SET COMMANGA ...........ccecsccscscsscecscsecncosensaccseesseencuceeeecs 4 
Serial port and Baud rate Options ..........ccscsesscecscoececsrseseveesucecesscucceseees 5 
Modem ptions oo. sicossisvicsecccrcsavcbes Saecnaseg oe dteas elles viele siuee es ote seeviaas sues ieee: 5 
Examples of the SET command 
Advanced use of MCLINK wis. . eccccncsas St teeeee Mevacor oi ia ee eiea bs ade buedbecesess 5 
Running programs remotely on the MC, HC or Series 3.............cscsseeeees 5 
MCLINK batch: files .......scs00c00ePhdeeee cee eet Meee tia nd De eiee sect ehate la ceses ness 6 
MCLINK command line processing.............cscecscsescscececsccsceccuteescnerscueess 6 
Invoking MCLINK inside an MS-DOS batch file...............c.csceseceeeecevessess 6 
MCLINK and «modems oiei.s cocci ivcesssrcen Petra ire cach eet oe eeees sdogecetes ade cdewtbeces Wickes 6 
MCLINK as a PC file server via the phone SySteM.........cccsssccscesceeceseecees 7 
MCLINK and modem Baud rates..............cscscscsescscecscsscveesececraseceusaceves 7 
Link on the MC/HC as a requestor Via MOCEM...........csccccsceresrssucecencncece 7 
Link and Modem Baud rates............cececscecscnessncacscecsvssceeseeseenssscucennsrves 8 
Link/MCEINK: with! MNP. s s0020c0.00c00.aceteeescteds oe PRUE tovdalivecediocsss 8 
Examples With :MOdGEMS: «...0<ccsevesevtarsdl code vevtueneadsvedevsedcaddcadadeaecaveveedaccsaceas 8 
Using a Dacom QuadPlus MNP 5 compressing modem...........cessseerseeeees 8 
Using an Amstrad SM2400 modem. ............cecscscssscocsecececeeescatececeucseves 8 
Using a Dowty Quattro SB2422 000. eccccscscscecsccveeeecsceeseveecensecees 9 
Using a WorldPort 1200 pocket Modem. ........sscecscrssstssscsevenccccasececeesens 9 
An HC with a Psion Quad modem ..............cscecscscececsvevescsceceesevensaversens 9 
An HC with the Amstrad SM2400 modem ............cccssscnscetscescesscccscsenes 9 
MCBLIRT CXC 28 ieee ccs ccaee Dt viene tins cues desides eve dswaieeea tev Ueactne than sec voeseepeses@ betes velebecesns 9 
Using: MCPRINT sscssccsiccsoeeeeptac cievtecbeves hes the cise do Sieb ave cedecasas deccw ys: 9 
Exiting: MGRRINT csi. .ccslsevesssceececcnaeecs sect eeeestecee Sei vciesbseter atta 9 
Printer configuration ON the MC ...........cccecesscesceescsceceestsceceecensesessenees 10 
Printer configuration on the SerieS 3...........scecscesescececscsstereceeceneesceeceens 10 
Parameters’. ivccs.scasavdtciSes wanes 2akeckcks eeeeee fae05 Jeni y wseee Fema tea Sea aee ete ose tae tee 10 
THe. < prdev->: ParaMeters...cc.scicccescacesstensveesonsssaudievandedenaveskscesdoeecedeees 10 
The: -C< POrt > PALAMECtEH,...ccceccccssceccecceeveveeasevesdsceevvenscoeedeuse vovevdauvecss 10 
The -t<tiMeOut> Parametel...........cccescecscescescencesccccereccesescensecesesssaues 11 
TGQ" Parameter’. siiccecvicasscuestetuis csana tienes oe vadee does Gen le cane sas boscuenseeemeesete 11 
SINK LOX is sisesseeceds canes esis tice wus sre ccewesvaeauees vend as daly cae''ss swaaddarev cateue Lvaue ooeoeobeeane 11 
2 “Resource Fil@s ciciacsssccsccvceses canes ce cede serctvcloseensccencsiclescSuscusevee cadet calaeenesgeioes oactee 13 
IMEROGUCTION SG vite A tecicic crews ies Ae ec ec po a danse eae seh ed edean ope aad eos sae gsree eee eenels 13 


ADDITIONAL SYSTEM INFORMATION 


Format of Sibo resource fileS............csesceccecescescecscescccseseecescsesnentseressseceens 15 
WhestormatOLAsCwtilOS sc. cece ccset cores roses cbcece x coinc covuiateruscus dead ca ute tos eecsae 15 
Some strategies for reading .rsc fil€S.............csccecscseecscececetsceccusesvenceece 15 
Example of reading resource files directly..............csccsssececscscevesesscvsscecs 16 

Using the rscfile class im OliD...........cccccccecececsscececetcecsecscnsoecteecaccecessececenes 16 
Basic services of the rscfile ClaSS.............cscscsccsteeceececscsssccecceusceteancaves 16 
Reading compressed resource files with the rscfile class ...........cecssesecees 17 
InitialiSing: aN rSCTIIC OD/SCT... cicc2s. cs cess cen dtecsis sae seoceunu Vadencstecgees baaGeessee ee’ 17 
Which header files are ME@dEd...............ceccesecscnscnccececscecscersucscaseeaseures 18 
Run-time errors with the rscfile ClasS.............ccccscscscsesescesececevecseecarecess 18 
Possible errors during initialisation ..............ccccscesecesecseovevscacersnseseseenees 18 
Eprorssduring tS readcOrgs ReAGNDUP <3, foci sicnesset eerie rene ctoststtaey aeacdesscss 18 
How rscfile errors are reported ..............cccscecsecscneceeesescececscecscseucssecese 19 
Dealing with errors in rs_read or rS_read_DUF........ccssceeeeeceesensceesceecescees 19 

Advice on where to locate resource files ............csccsseeceececsescsscnccccsacceseeces 21 
Mono-lingual applications ........sccsssscscscsvssctsscscascsrencetssaccsescsececsccerecss 21 
Multi-lingual applications ..............cccececscecececenecscecatcuceccetevscacererseueecuss 21 
Copying! Of applications + sicielisccaccis vies disascacaveciaeacs oe dedasdacvazathasstee evens 22 

General comments on multi-lingual applications............ccececesesvercecseccesvcscens 22 
The basic principle of independence of code from resource file .............. 22 
Careful design of screen layout ...........cccecscececcscsecsceecscecsteveseaventsatececs 23 
Codesize’problemsitisv.... 203.00 ne a Ee Re ee 23 
MAPYINGRKCYDOSrdS sic sieisevescdscdeccestca consee cere ot oa tee a soascieteleel ber ee tecct ease 23 
CONCIUSIONS 2: cccacedectsssccadeccutaciscaseehestet tase atte ec estoe rte conten merit rene cake 23 

Creating .rsc files USING FCOMP.EXE..........scscececscecececscneersccceescseveseseetensesses 23 
Generated s1SG- TIES « «cisco. sive necdendocseeiescccecssanedsostacesucesaceeseouessvevessdenes 24 
The syntax of the rcoMp COMMANGA............sscscscecseeeceasccovarcvevscesccssseses 24 
Include files within a reSOUFCe SCIIPt ........ccccccecsecseecncscecceascoeeecscesessees 25 
Conditional compilation in resource fileS ............csscsececseecssevcesssensesoeenes 25 

Contents Of <(SS: TICS 5. oc ccsacie.cosvcdaccvtedest vee es cane sceeecedcus vans ise sagcesasieaadassaves 25 
Declaring STRUCT wciccccvesdecessccccvecsenseuscivevescscevs secs cevavivesdecsevencseveds. 26 
Possible member types in STRUCTS..............ccececscsecscecentsecrcessecncaseeeees 26 
Declaring?RESOURGES 7h 5s. PRA rete its wnyetacevedes sescets cenecd eves 27 
Declaring the values Of SUD-STRUCTS...........cccsscescetsescteseeseecsenssecnscees 27 
Leading byte and word length values...............:scecsececesesterevescsceecseasaces 28 
Arrays within resource files ..............ccccsacsceceserecsececensctccscescssececsasavecs 29 
Creating SYSTEM resource files ..............sccccecsccecsscsceseeascnccececesuscseeees 30 

SaeWV DR BMINtinG) .22ci00c60 cs 5 cancccsecctossceccscviceasseeteacsscechotaberduscscuscadeevaccesoeteesraetaes es 31 

INTRODUCTION oi. vols edeis oa cases sodceun casts au tene trams taal oue tes aonon tet en ees based wets aseees cs 31 
Creating: . WO TIES 65s catccucces i orek cond ss eee cree oe eed Saag OE eae et oats 31 
The WDR printing environment variables.............scceccscscscseescesssenceeesenns 32 
A note on reading environment variables ...........cscscececcevcecscsesceureceeeeees 32 

Overview of the contents of a .wdr file...........cccccsccsceececescsenceetecscsscseeses 32 
Deciphering general. wr ...........cscscscscsvesccececencsesceesceecseterssensscessaseceucs 33 
The: Header PeSOUrCe co .sc5is cust eecve covcews Does MRT Weve vote da bee ce valved Mielvcacsoess 33 
ThE COMMANAS FESOUICE........ccccsessscccnccestncenvecsossacecesceseecececeesssussensese 34 
Model resources v2: 2s; sscctteess secs cecuaseadsecte ates sswsveceusets vas ceaedgittieeadevtas scees 34 
TY¥PCTACEsFESOULCES so: else ci ssc vedcceseetucescascatnast certeee holeeiebes Tema acti helacns 35 
TRANSISTES [ESOULCES 2505. <cscesicecevazencvcadacn olecntenseskecuaued tec ctacdwcesteaetensbes 35 
Summary of resource types in a .wdr fil€..............cecceecececscececeeesecssesees 35 

More details on the contents of .wdr fileS .............ccscscecscscncececececscscnesceeees 36 
Possible: Wdr=flaQs .22..cccsetc.ccce coi leceactededacesvsrecstevancaesasvandenusnbi ceed vedvets 36 
The notion of "printer models” ...............ceeeceecseneceeeeecerecesersseeseeceasesaes 36 
Overview of the different CoOMMANA SHIriNGS...........cecscecerecscerscecvessvescess 36 
Special characters in ComMMand StringS .............cccecsececsecesscnsscsceeererecens 37 
The MOVE RIGHT Commands............ccccsscscsssseesssereccersvcseeesesecesenees 37 
PRiMterURitSis. <a. esSeescend ic cvessse bites iacesb vee sdadesdsteaegeeeedseteadaueswerss ablecusos 37 
Possible; moOdel=tlags yi ic. cis c.cciecccacs coaticaec cnc sts esenvees weores spaitgaestonctenscicess 38 
Typefaces ANd TOMS os. eas ececcecve ce tacden¥esceses’ss4esws bee bl bedesd cesuw es caaseesoayers 38 
Ty petaceiMuUmbers c.etiv..iscadss swacen se oesdees coeds ceatwertalSveves lewcvvegscasdensoness 38 


CONTENTS 


Possible typeface-flags ...........ccceressonscersescssecetsecscecesereceversessssencarsacs 39 
Heights. Of fonts ss sre occ iccsheoccadowyn acces canes seeteateucs oateec beuodinee ek tkedalvaane 39 
Widths of characters in fonts............ccccscsccecsscecscccuecescuesscceseveseusassenees 39 
Creating .wdr files Using WdtraNn.exe .........ceccsecsscoersceecesaececccescesevsscsssesees 40 
Contents Of. Wd sfiles ic ciccses.ctockeocecsscss cs ath eae eae SP, 40 
Acceptable syntax within a COMMANDS resource definition.................. 41 
Acceptable commands within a TRANSLATES resource definition........... 41 
Acceptable commands within a WIDTHS resource definition .................. 42 
Acceptable commands within a TYPEFACE resource definition ............... 42 
Acceptable commands within a MODEL resource definition.................0.. 42 
Allowed typeface NUMDETS .............ccsccsecsccestseceseecesuccececaveeccesssaesonseess 43 

4. (DBR eS oic sie sc citisc ci conn ceed sedi cesecatcsics coy oddeceda ve vedas seateneatseate seteee tae e ee cca ee Rein beke 45 
IMTFOGUGTION ccs cat sccossvcrdesgenedsvetscn cecetercneagetvetessed csiansecusesecisuambecasecurisncases 45 
Basic structure of DBF fileS....c..05.cccesedccvascesvevsvecsabsd css ees bettie Peres ccawee 45 
The: standard: header sc.....sssscie secswewe clic egessacen tacchtes paeeETs Mac et code mt ese Veeees 46 
The, extended ‘headericcsschisccccssstecevvagiveasacact ewes veceveetttbeweel thee esuvdecesan 46 
The field information reCOrd...........ccscscsscetecsscuseesscvcscecseasssoesessesenscncnss 46 
Thesformat- ofvall reCords si: csecsccuscnesevdbecesedv ovectveeeseeous coleman sso lineven 47 
Deletedirecords:osicc. ans. oscis ecco eeecunn coaccu dv acee seh sevesttee seh ctecn Ae at vedas 47 
Descriptive reCOrdS «05... ssieccecacuesdueeviseanetsseatesssoocsoseescieccceciececesueeeeans 48 
MoresOn: type: 1 *feCOrdS). iii. ists icvecssecsewicecesvsesvececersicseisusadadvevesisasctoces 48 
The Series 3 Database .............cccccecsscscaccscsseueccetncessccsesceccessecseneesensercens 48 
Field information (type 2) reCOrd ........cccecessscevscecsceesececessesecacecscesrceences 48 
Extended Meader oi. ceic.sccesevsssccesavevescscasdeuciiades oda vavcdesbexeeinensevduscvasvedws 49 
Descriptive: (type+3): record scccsvecsteceestacetereveeie os eee eeeete Tee 49 
Flags options for the Series 3 Database ...............cecscscssevstcecececscceeeceees 50 
Flags options for the Series 3a Database...............ccccecsccesccecscecesavcecsecs 50 
TYPO AV TSCOPdS i 2.30556 odsisesietvaveve vcr ocestiocvedyvevedeesteseueesss Si0s ces uiwdivewesscdes 50 
Continuation SUD-fieldS ..........ccccsssscnscecsccecstencsssscecesscscecvcessestscesseeeeses 51 
The: Series: S!Agenda.c..c.0cb seven cte cA etek clots essen Chav aes bea tn au bhetuoees 51 
Field information: records: Sete. ce ne ST siactics ab koesues castes tisccceavescvevecs 51 
Extended iNeaGer i csc.insccatevetectn eeter rete caches ctecasee cuss sianca Pe asteet erates cvasens 51 
DOSCMIPTIVE FECOMG (oi sco. ea chil heeee aad tes cvceve cote coacue od eed a beeas ck oen eb tei uae 51 
TYPO PTA ECOLdS 5 cs az. cece dan Seesaw svocd vcs ccte Rete a eee See ee aks ee EE an eed 52 
Calculating with AlarmTime...........:.:sccsscescescoscercnesscesscsuseccssncessessesacs 52 
The text Of AN APPOINTMENL ...........cscecececcausceeccrensctscuceuccscecceseccecensaees 53 
TODO ATEMS 2 65.0.05650 cP MEA, etic eee el sk Bed satan inedouss 53 
REPEAL ITEMS, ciiecescensvssenees evades scscsuasdesesdcsesaaarencessvuucacauasegaceen abe monneatede 53 

5 Series 3a Agenda File Format .............:cscssscocssccecestontceceessscesecnssescccescecescussecees 55 
Basic structure of Agenda files ............cscsccecsccecsscecesacccescacsavecevessercetscsensss 55 
Phe sStandard: NEAdSr osc ccs.co.csccenncseoteetesseaescacwacseracdouvestiecacnceesseacene 55 
The extended header ........csssccscscsscstescscerssenecscatacceecessanceeossccsacesesenace 55 
EMG! Gata reCOrds si. 3 iccscsvoil cabataveseyevoneas eicouss dacs ediewe Concnsas sane bowstncdesets 55 
RECOrd TY PSiss.cc sacs casusnieodencugeudads sven sueuedencnededesdvea cue cdstaucdies dened Mewscotteedys 56 
Type 0.= deleted reCord 2. vccccesnciccecsccsceck ccossctecacdestsvecesedevccessesedicssisseenivdes 56 
Types. 1 to:4 entry records ccccicissccseesceacists SEA ed ed sia eas as vevtentecsouend 57 
Entry.detailS: tleldsscse..< ca, dona s Bere Mises dee S een oe sos ete tias eco ee falls ER SRERE awe 57 
Aheztitle Field A seceececcscaewesietoteecussateuroed anes easbaaes cbeenaee vaniaehub ein redness 59 
Phe alert Hel? asc. se cieeces ceecacrvuetavs cove ence cabeeede s¥eWw cossvcec sass devbececsacs 60 
THE MEMO TED: Mess cect seas cseascieeccvasstaran. voseceteres dbececeetese ee toesc ten robes aes 60 


ADDITIONAL SYSTEM INFORMATION 


TY PO.5 EPCOS s vie Soa ca sevens veVecacemeecedesdetedieesleasaewetoe fetta ettlad oo Taaeoten, 61 
Type 6 - ANONYMOUS ata ........ cc ccecscccscetceresecseaccscececeteceseseceseseseteeaeaeneeees 62 
Types: 7 and:8 ~ reserved. evs. isivsece cede ci tideens vocnts cis olsclenstets ciiesedsvenssescstes ns 62 
Type 9 - to-do list information ............cccscscsccccsscectscsstcesesceceesesssaseescssenens 62 
Types 10 to 14 - descriptive records .........cccccscessccecececscecssecnssseeeeatotesaeneas 64 
Type 10 - styles descriptive reCOrd.............ececsseececscececerecneeetetescsseceserevecs 64 
Type 11 - to-do manager descriptive reCOrd..........ccccccscecscesecscececeeeeateseeuees 64 
Type 12 - frequently changing data descriptive record ...........ccccsescsceeterecees 65 
Type 13 - general descriptive reCord ...........cscscesccerscserececsececeseecssonesseersenss 65 
Diamondélistssetup*field sss 2c ie tieess otis eteis csi a eedeaeede tever set MA Aste 65 
Day entry defaults field ...........ccccersessenecccccerersretecececseecacessceteatetessseses 66 
Anniversary entry defaults field ............cccccscorscsccscecvcrsssetatssssecsacscessees 67 
Generaltdefaultsifield ters nec Pn aR cae Seis ccde ceadahaoteeatatetecess 67 
Day ViGW:-SCtHNGS TIGIG Si ics. cs cesccesecheeedsccensvassedeeeee Tie stuavcaie tees Js ota 67 
Week view ‘settings field. .........c.csccscecscscscecseqscnesedevdsescortcdedeeseterseatcers 68 
Year view: Settings Field ins... sidess cesses seat ss stvonds cceesteGssceevader Tier basceerosdese 68 
TOzdO. VIEW SCTHINGS, TICIG soi csicn tsa cacedenedacsttesiatiesaess cokasasestecesdattsteetesses 68 
Anniversary View Settings field .............cecccenesscssccnscecnesccessesestecetcceaners 68 
List VIEW SETTINGS TIEIG.s... ic. .ccntscsassteessecscsticeesccncas cecsdssesueuneseesreveceestars 68 
Type 14 - print setup descriptive reCOrd .........ccccceceseecesenerececececcseterssseeeeees 69 
TY Pe 152M Gal cs vec ckieiseteae decade vdeetSansedsacetvssessvaecyetioeosnseascasseavselesebces 69 
6 Word Processor File: Format 2... 3.0cc ccc. vcscccescsdescccnscelscsevcisccsVorevetedvcrecesesessssactecas 71 
The-document- head e@tssvssvscissececrevesrrsaereraveseverestyreveeeeitec ae eerste eer eT 71 
RECOLA TYPOS: conc cc cccccdssorscsteve cs dveseecanesstedarevesseteegisrseeeberteeeeuasseaesureseceetyey 72 
The options data record (type 1, length 10) .............c..cesesceseeceseeseseesces 72 
Printer-related data record (type 2, length 58) .............cecssevcscsessseccscees 72 
Printer model data record (type 3, variable length).................csscsceceeeeees 75 
Page header record (type 4, variable length) .............cecessseseseecscscscsseeees 75 
Page footer record (type 5, variable length) ..........-....-cesssecesseeseveseeereces 75 
Style data record (type 6, length 80)...........cccccecscscececscevevsseseseseeceencnen 75 
Emphasis data record (type 7, length 28).............:cccesensorsssoterecsceseataces 76 
Document text record (type 8, variable length) ..........c.ccecsesceceeeeneeesenees 77 
Document index record (type 9, variable length) ...........cscsecssesscseesseneees 77 
TEMplatetil€Stitrcrscctecsesavcistsiscresscceesttcctertotertsrttterstistsenseetesscrcstvorniesion cs 78 
Printer Criver fONt MAPPING.........ccceccccssecscsescueseesessecscesnesssecteseseaeacecerectoss 78 
7 Series 3/3a and MC Spreadsheet File Format .................cscecscecscncscncscscssossccnvens 81 
FUG MOS OR cee: sews enstiiesveneevnsetesbiveesalsecaaters caseueettecdccddacedehevt coseousecvevedoctoaxe 81 
RECOPGS west ves scvcccccdvenccuesceeevedswcccsctcswed secs vets sua Miuadsssebncsrdeucececdscededeleud 81 
Range reTerenCeS.. ioisc.cnevcsactneesaveisececccesevassesececactsesacscecaceeeveasinaseedees 82 
Formula record (type 1, variable length) ............cccsesescsescseeeserererecseeesees 82 
Cell record (type 2, variable length)..............cccscecscscecscecscecscnessessceserens 85 
Column width (type 3, length 2) .............cccccscscsesecececeeteeencececssensseceeees 86 
Default column width (type 4, length 2)................ccececeeceesreeseesseseesees 86 
Status information (type 5, length 4).........ccsescsesensreecerserenseseevseceeseenes 86 
Display information (type 6, length 26)...........c.cccscecsescseseeescsrersetsoeeeees 87 
Named range (type 7, length 26) ...........cccscscsessecsscceseseserscteesetseataeerens 87 
Print range (type 8, length 8) ..........ccscsccesecscececsersesstcescessceseeeseeseseeeees 87 
Database, criterion settings (type 9, length 16)..........ccccccsssecsceceseeeeetees 87 
Table information (type 10, length 16) ................ceceeecececeeececeeeseeneeeaeas 87 
Print setup (type 1:1, length 2): ciceccccesscaacchesecsctessbadhen cdedeesteasegeseevdeaace 88 
Eont:(typest:2 length. 8) oes ter ctecds cee cane deteatasescsagiastasadantecstvn.verersess 88 
Graph (type 13, variable length) ............cccccsseccseerseeeseescessceterssseesteeaees 89 
Current graph index (type 14, length 2)..........cccccscecscseescsceecscensnesceeeoes 89 


iv 


CONTENTS 


Font palette (type 15, length 24)..............cccsccesssscerercececavensecsscusesesceees 90 
Print data (type 16, length 58)..............ssccsssscsscusnscvcovevesscssseeceveserceans 90 
Printer model (type 17, variable length)............cccscececssscccevesesececeaceeevess 90 
Header text (type 18, variable length) .............ccscscecscscsccscscevsscecsasecesees 90 
Footer text (type 19, variable length) ...............cscccscsuscscscvesnscesesecseceuces 90 
Display extras (type 20, length 2) ............c.cecseceecscecsrencecvecessevsceneaueeess 91 
3a Display extras (type 21, length 4) ..............csecscoreceseseeeenscunsescececesees 91 
Password (type 22, length 18).............cscscssssescessevencessesececusucsecceeecesens 91 
8. Writing) Device Drivers iii .25. ins sbi oso Sooe ck Site saci css cacsnseadeccasscticces eitets ctvs sweetie 93 
INTRODUCTIONS. 0255. ccecardicaececedvore red eaeavedesue eet be Tore loth eo tee score tes Per oh not Sveti 93 
The location of device Arivers ...........cccscsccecsecceseccasscecescsceccscecersenceeses 94 
Device Driver Names. .........cccccssscssonsncssccscsseuecsceccecencecceccucesensatesseseenes 94 
Device Driver*Ghannels:stesvrecetn eens oe ade eo ons ea asd eee ea nae’ 94 
SearchingiforRDDs ici. vccvsesatevs cevaceecavesesaveten see vase eee eee a eal! 95 
Device Driver Hierarchies And Attached Drivers ............ccccccscscsescsensecees 95 
Interrupts and Interrupt Service ROUTINES ..............c.sescocossenscereseceeccsevceensses 96 
Device Driver I/O Semaphore Waithandlers ..............scccscscsoscecscesecscsacees 96 
Loadable Logical Device Driver Structure ..............sccsessevcscecececsssvcnscecesscescs 97 
SINGICT COGS SEGMENT. .is..cc2escsvencace siete seetervecepascnecssdeenonreionseoetantetMestssee 97 
MHeEsMIDENt: SrUCtUies 525 ii. ceva s cscs cdeadseeweehecascecdanceedlissicnccdendacieessceuteess 97 
Mandatory‘ EDD: FUNCtIONS «6.060.005. sscecsnarecescscdeseanctasedontorceesstonsveeaecseoeve 98 
DEVEUNCINStall meres cce. i cccn ovensssl<cencrceas soeescescesos soedensesvact tasesestsaneanwadaeeeds 98 
DEVEUNCREMOVE ai.52.ccscccs ssa ce cvocsteeedaedescstacGennuedases ees wdiweceue sees dae cekvens 99 
DeVEUNCHOlde in. ciccceccrcscncs yeh actebatesenceecevesssccees seeaees vas csaestuenadisaulennbadee 99 
D@VEUNCRESUME oi. cvcsicccesncsecscbescssceascscssieccccsccdcaddeasccccloeceecddcceatwetacsive 101 
De@VEUNCRESCE avis. scucssecetascecves srassieds cae 3ss0cnehveedssdleadcccgeesavavinwedesiexvers 101 
DEVEUNCU RIS weve se cuis svenevasscavensvcdtedgeeseeuded nace cagece dec entdacau lee teeasenue cues 102 
DEVEUNCOPEN sis cisceeccce ta caevocadaasisaessccuasasuavieoecceedteeten snot caetaet oufeatieateass 103 
DOVEUNCSEATEGY: ss cence ececcssetacasseveids Ptecectescuetetass foes daseeceteleseens MetNeauaats 105 
Loadable Physical Device Driver Structure...........ccccccccccccsceccccscsccccececesacsecs 106 
Single Code SEQGMent ..........ccssesecscecscaceceususcuccsescecccccesancetecusececeeecesess 106 
The BIDENt StrUCture: s vscccccesacsceccesscctetcs. caus ccoctresasuscvactsvstecectstas de wcecs vs 106 
Mandatory PDD functions ...............cccesecsesecececsceceescscccseterecseecsasanscecs 107 
DevFEunclnstallPDD on5 55 scccccacccscvecccuccvevnevecevescncelswoesedescsessechcoscdsesnsceess 107 
DevFUNCREMOVEPDD ..........cccceseccsenescercccceavscscscsceseucesssacscesscsersceesesere 108 
DeVEUNCOpenPDD ii... ceseccedsccesssseses see pensesvicscgedsintadersbicvaseusrtisansn eee 109 
DéevFuneStrategyRDD. esses oeeccacestssicseeseveasanesa cOSveedd a doeaas sdesee ohavero eee se 109 
9 Example: Device: Drivers ci. ceccccsecccxcevcscscosdevoscasuesodcatasudaccascdenuacteoeciaedvevedeSeusees 111 
An Attached Device Driver Example ...........cccesecesccecsscececcenscesccssecssesscceeuses 111 
Thesde vice: table iis.5.2 cise i te code eciwiscacececctceves cocacecosccstiteucacteudesdelecns cas 111 
TheslnstallsFUNCUON we vevesvecssssicesasvavavesesesesese sees covasnce cos sessesceesdieteereeacted 111 
THe Remove Function ay..ecccicssciescucetecscescedsccdscecess nave lgsWeevdcticiedssst ones: 111 
WHE HOMD FUN CMON wes sees Scccsvscuveeccesvebeuwessipecevscansasecersregeedes Meee socehe bees 111 
The ReSume Function ...........ccccscsesessceavccceecccsreceseucescaccccseeceaceseueecuers 411 
he RESCH FUTICTION s sewssvissestececcsceedofedeotels ieee asuon sete sia eee ee 111 
THEAUMITS FUN CHONG ese Scceeccn ceed scccavavccecedabcecasevcdeoses tec sctlccnisezdefecescants 112 
THE OPEN FUNCTION vice. csiesaseevesavcevesneccececcuscesbevetacanechuasaececedscbasieaccceenss 112 
The Strategy FUNCHON .iicccseciecicccceeeeccdsuchwasievccsedecenestsdessdussevasasdwededecs 112 
The Wait Handler Function ............:cccscscesecsccscscscscecssececcvcesscesaeausoucenes 112 
Non Interrupt Based Sound Driver ..............scsccosscsecsestecsrscnsececsesceeveseeensens 113 
Phesdevice table ses. scscsdscsnecaaveteea vesecereseasevewnesddecdesacveceediaodseecced cdsceued 113 
Ehe: Install PUNCUON . 3.005660 scacevecesad chee cedewancedscteeschssadevssabiascscecdsandecsans 113 
The Remove Function............ccccecscscvecsccccvansccsesccusesceseceeasvesanevesensecuens 113 
THE HOIGIFUNGTION. « ficiedset ees tis cos ee ne tencc eee veadeeekencades sea dciexdeedaatheteexsesoweos 113 
The Resume Function ..............ccccsccecsescscecccccssscsceccccencesnccsssnseesseessaces 114 
THEXRESCE- FUNCTION oic5co3c20 sacks bo celeb ode ductecdieseceuesedavestinuxieweusewanedesmeendea’s 114 
Tes WMitssPUNCION Sc. oeaste scene sec te ee See cece aca cates tease dobdbcasenadtcssesesstaundsots 114 
THE OPC MIEUNCUON srececccvecssporesscrsauecnceweutegecdutesesisiced @ocdeetess iaaces tune axe 114 
The: Strategys¢ RUA CON co5 res. fhe sax. cescecevecsvesdhccstececieok sleds etotaes ves onies ooaene ds 114 


ADDITIONAL SYSTEM INFORMATION 


The: Wait: Handler: Functions... s.seio..otee et eed Stee SPE ST eee ees 114 
EXERCISING: the: VECIOIS Feces ener sseetiessccsscetterectacseteoncecsieeesene cette sete wieter ats 115 
Interrupt Driven Sound Driver ..........ccsceccecsarsccsccecccececsnensseerscenssseesesseesons 115 
Thecdevice table sie co criscoccccccccnceca cesses ccoke cuveeces Suuzte lens cess ev cstetoaseteeds 115 
TheslnstallFFuncton Aik ove, cee eeaw as cevarsocceeees ec ecsees vecuatensctsnceceuece ccs 115 
TERREMOVE FUNCTION ci nese icteet nes ccce ch ease ccna fe ceecmeesevesceuddcsineetaetsedecuecats 115 
GHEHHOIGKFUNETIONG.. .ccctevcessnecoctessexercewsataceces tears ssissadecouse ocesineu dec cet te cs 116 
THEIRESUMELBUNCH OM Mec.ccs sca cescesancectcccccecvasetestsecodacnmsitetcanestscisecasesees 116 
TRENRESCTEUNCHON aii e.c3 si lesceccecdssdestesescaes caacesenceves feser core sista se duentecever 116 
THELWNAITSTRUN CHONG Soe5 5 2oiceadadiieedeneveecs et -ntianen tagcat gabe tee steers tested) sa 116 
THEOL ODEMLEUNCHON F ooveccaeistcocae tees aceuceusladdaei ils ones oaeeace cetaceans sees cnn suneeceees 116 
The StrategyitFunCtOn). co sccccediwesescusceesecssce sche saswendnenensdeestisveeesnatsteueres 116 
The Wait: Handler Function. cs... ..scieccciccscccctcadescdadevctescstecssteteesesttetcedes 117 

10 Word File Format Conversion DYLS.............:sccscescscsssceccccccsceccsessnsccnssascassnesesens 119 
SAVE\INTEPTACE: SEPVICES sic iicdeessccsscccesessccearseecsvccvsdseseestecesosvetenceverssdeesesedes 119 
Present file OVErwrite GialOG .........cscccscscsscccesccestcsscsaescesceascoesesassesessses 119 
COUNT INDEXAAGS vases cc cc Seccea Sect cote n ac cee aee nea cas Pee eee Mca b ec oebeseneeecewes 120 
SONSE AN IMGEX CAG re: oiases ce ceie inca cecashosnaedevsncconsesdavasevedscucerertscgawenseeees 120 
COUNT Paragraph-StyleS \<. 0c... cccasccssecdessscsesavecrecsctaneccteedsvecettonesrctoces woes 120 
Count EMPNASIS*SIVIES: F...cceveseccs chs edsesescaete wceeeracesgensietecereiseausescsetees 120 
Sense a paragraph style by index..............cscceceecencecseeecneesunecesesceseceners 120 
Sense an emphasis style by INde@x........ccccscsccsscsacsscscecssoescsseesscveaneeeees 120 
Sense a paragraph style by short Code ..........cccecececseecscseseereessoreeetonsees 120 
Sense an emphasis style by Short Code ...........cccececscerecereeseeerecsoneeaeaees 120 
Copy document text to DUPPEr ....... cece ee ee scene eee eceeencnceeteeeenseeneeceeesens 121 
Set afileverrOns: eescecizs foes oswi avs tack vase tabs saewtadeaededsaccadsucnstverce rector esate cass 121 
Load jinterface:ServViGeS:.....c.0c ccs iswctesseddecacadscsdscdederenaderntesacreeesat esse beceseees ed 121 
CIOSECUIreNntly: OPEN TICs secessccdees cose iene clbeterbacstvecnccadenssebeteodscendedees sei 121 
Record name of file ANd redraw ..........cecesecceceecssceceeseesceescecreceessecceseess 121 
Append a paragraph Style ...........cccccecscsecsccscscnesccsceessenseccescseesesteesesens 121 
Append an emphasis style ...........ccssscsecrscenrscescscrscesseescececeeseeeseetecemens 121 
Apply a paragraph Style..........cccccccscccsenscesescecneserseensessssnessoncnseseseeeoess 121 
Apply ansOMPHaSisSs si ceiieis seoccdes isa eacacesdedeccnes cocesieteactcvesatsceedocesvened 122 
Create: default Styles .....c.cccssssccctesececsaasscdeccccsevecdacentetesteleasdacdusnscsoeees 122 
ISOPT TEX Gace scccteecrcevossccsnecacecevensccactieinscaeucospiwecetecdersteitessccsonesseuans 123 
Delete: Text nce. coleceSesvatecsvanaessaeedeiiacesndcueceedessssesdaceecssseaetaierssseeacesst es 123 
COMMON interface SELVICES ........cscecsececscsscctsceceecessseserecossscesenteneescensensenes 123 
Start active object file CONVErSION .........ccccscsececenceeteceesececsetenoeeeeesosaeees 123 
Stop active object file CONVErSION ............. ceca ececessenceeeencssteneecseeeeerenes 123 
Set/clear Busy Status vsecscctive visiaievawedea cases cube ss Se teacceeeslndteuts Peiweeedecahe 123 
Senserprinter data yc... cece c cosas ves cevisedsivesvssaecsvedeusescudetevensereceeenceranwebes 123 
Sense printer model data .................ccceeeeecececeeceeecereceeneeseeenueteceeeeeoeaes 124 
SENSO PINTER CIVEN ss cec se vcscssteccentetevetesenssaevescsascnes da cdcacsussesvasisesesegee 124 
Example Code isis. icdisc iced Mitten cities deten a iedsiaa cs secwavagebecevewedecesisaneetssietess otletes 124 
SAVE DIAINtOXtiai ce cliees ces ceeesiacdeekosn casnsccecevescacetecdescetancertenteree sowssanset 124 

LQ ad PIAIN, TEX ccs ictc cave sais cs caaic bY ea scien ve ete wc siaw's cule sine’s Seecetedsdledvcets Shan vee Petes’ 126 
Debugging a Conversion DYL............:cccenseeecsc recent eeecesencetscesnesesscetneesereees 129 


CHAPTER 1 


IMICLINK, MICPRINT AND SLINK 


The directory \sibosdk\sys contains (amongst others) the following programs, all of which can be run on 
a PC connected to a SIBO computer: 


mclink. exe a program allowing file transfer and remote file access between the PC and the 
SIBO computer 
slink, exe a “no frills” server-only version of mclink.exe, which may run on PCs or PC- 


lookalikes that cannot run mclink 
meprint. exe a program for printing "through" a PC to an attached printer. 


Basic information on connecting a PC with either an MC computer, a Series 3 computer (see note 
below), or an HC computer, is given in, respectively, the MC Operating Manual, the Series 3 User 
Guide (see note below), and the Introduction chapter of the HC Programming Guide (part of this SDK). 
The information in this chapter gives some more advanced details on the above three programs. 


Note: throughout this chapter a reference to the Series 3 machine is taken to include both the Series 3 and 
Series 3a machines unless explicitly stated otherwise. 


(a a eee a ee 
Miclink.exe 


MCLINK requires MS-DOS version 3.2 or above. MCLINK is unlikely to run inside "DOS emulations" 
provided by other operating systems (though it happily runs inside MS-DOS tasks inside MicroSoft 
Windows). 


If you experience any problems running MCLINK on your PC, you should experiment with reduced 
contents of autoexec.bat and config.sys files. Serial mouse cards may be particularly prone to interfere 
with the operation of MCLINK. 


If all else fails, you may wish to use the alternative SLINK program, also supplied on the PC MCLINK 
disks. 


Note that, by default the HC and Series 3 run at 9600 Baud, the MC and Series 3a at 19200 Baud. When 
a PC running MCLINK is connected to an MC, either the PC or MC end will in general have to be 
changed to enable a link to be established. 

Commands provided in MCLINK 


When you start up the MCLINK program, the lower window contains a$ prompt. At this prompt you 
can enter various commands. These commands cover: 


=  file-handling 

= changing the communications settings 
= exiting the MCLINK program 

« displaying the version of MCLINK 


= running programs on the remote computer. 


ADDITIONAL SYSTEM INFORMATION 


For all these commands: 


= the command can be abbreviated, to a minimum of the first two letters, eg DE for DELETE, RE for 
RENAME, CO for copy, and se for SET 


="  CTRL-C stops the command (though in multiple file operations, some files may already have been 
copied, deleted etc, before the command is stopped). 


MCLINK and single floppy disk drive PCs 


If your PC has only one floppy disk drive (currently referenced as "A:"), and you mistakenly enter DIR B: 
while in the MCLINK program, the program will be halted by MS-DOS, requesting you to insert a disk 
into B:. 


To avoid this, use the MS-DOS assiaN command before running the MCLINK program, like this: 
ASSIGN B=A 

Then DIR 8: will be read as DIR A: and the program will not be halted. See your MS-DOS manual for 

further details of the ASsiIGn command. 

Exiting the MCLINK program 

Type EXIT to return to MS-DOS. 


Display the version of MCLINK 
Type VER to display the version number. 


Le ce Se ee ae ae eT 
MCLINK file-handling commands 


For file transfer operations between a PC and a SIBO computer, you would normally use the File 
Manager on the MC or various file options on the Series 3. For certain purposes, however, you might 
choose to use the file-handling commands within MCLINK on the PC instead. 

Rules on filenames 


In order to make full use of the file-handling commands of MCLINK, various rules about filenames need 
to be appreciated. 


In MCLINK the syntax of full filenames on the PC, MC, HC or Series 3 is: 
filing system: :device:\directory\sub-directory\file.extension 
This is very similar to MS-DOS, except for the <filing system> prefix. 
If you do not specify a filing system in a file specification, Loc:: is presupposed. 
In MCLINK on the PC, <filing system is 
= oc:: for files on the PC (local) 
= remM:: for files on the MC, HC, Series 3 or Series 3a (remote). 


On the MC, HC or Series 3 the situation is reversed, with Loc:: for files on the MC, HC, Series 3 or 
Series 3a, and rem:: for files on the PC. 


Note that for all MCLINK file management commands: 
= if no directory is specified, the current directory is assumed 
= if no device is specified, the current device is assumed 
s if no filing system is specified, the PC is assumed. 


When specifying a directory or sub-directory in the file-handling commands, make sure to add a \ onto 
the end of the directory name. Otherwise the directory name will be taken as a filename. So: 


COPY A:\*.* REM::\LETTERS is wrong - it would try to copy the files in the root directory of a: 
to the file LETTERS 


1 MCLINK, MCPRINT, AND SLINK 


COPY A:\*.* REM::\LETTERS\ is right - it would copy the files on A: to the directory LETTERS. 


Syntax: DIR filespec 


The directory specified may be on the remote or local filing system - eg DIR A:\LETTERS\ looks in 
directory LETTERS on the disk in drive A: of the PC, DIR REM::A:\NOTES\ looks in the notes directory on 
the SSD in drive A: of the remote machine. 


Use wildcards to list only certain files - eg DIR *.TxT to list just the .TxT files in the current directory. 
When you get a directory listing with the o1r command, the following file information is given: 

« file name and extension 

= date and time when the file was last modified 

= size of file in bytes 
and a combination of these indicators as appropriate: 

Mod file has been modified since last backed up 


Rdo read-only file 


Sys system file 
Hid hidden file 
Example: CLIENTS .DBF 14/01/90 09:54:23 544 Mod RdO 


Syntax: COPY filespec1 filespec2 
Optional flags: 


<i include sub-directories: If there are any files copied from subdirectories they are placed in 
directories below the current directory, to reflect the source directory structure 


-m modified files only, eg COPY REM::A:\*.* -i -m copies all modified files in all directories 
of the SSD in drive A: on the remote machine to the PC. 
Examples: 
COPY REM::M:\*.TXT \BACKUP\ would copy all the .txtT files from the internal disk of the remote 


machine to the Backup directory on the PC 


COPY \SMITH\*.DOC REM: :A:\LS\ would copy all the .poc files from the sm1tu directory on the PC 
to directory Ls on the SSD in drive a: of the remote machine. 


If you do not supply a complete destination name, the root directory and/or default disk on the remote 
machine is assumed, and the source filename is used as the destination filename. Eg 


COPY \HOME\SECURE.TXT REM: :M: 
would copy SECURE.TXT to the M:\ directory on the remote machine, giving the file the name SECURE.TXT. 
If you do not supply a complete source name, the current device/directory is assumed. Eg 

COPY *.TXT REM::Mz\ 


copies files from the current directory on the PC to the root directory of the remote machine's internal 
disk. 


Note: if you are transferring a lot of small files to the Series 3, it is a good idea not to stay in the Series 3 
System Screen. This could take longer than usual because the System Screen would continually update its 
file lists as files arrived. In extreme cases, with lots of very small files, the file transfer could even fail. 
So press an application button, such as the TIME button. 


ADDITIONAL SYSTEM INFORMATION 


Syntax: RENAME filespec1 filespec2 


You can rename a file in any directory on any drive on the PC, MC, HC or Series 3. For example 
RENAME DETAILS,DOC DETAILS2.DOC 
RENAME REM::M:\CLIENTS.DBF REM: :M:\BUSINESS.DBF 
You can rename more than one file at a time. For example 
RENAME REM: :M:\*.TXT REM: :M:\*.DOC 
You cannot rename a file across directories or devices. Thus 
RENAME REM: :M:\LETTER1.TXT REMs:B:\LETTER2. TXT 


would give an error. Instead, copy the file to the new name and destination then delete the old file. 


Syntax: DELETE filespec 


Optional flag: 


-j delete files of the same name in sub-directories, eg DEL *.TxT -i would delete all .TxT files 
in the current directory and in any sub-directories of the current directory. 


You can delete files from any directory on the PC, MC, HC or Series 3. 
You can delete more than one file at a time by using wildcards. 


You cannot delete directories with this command. 


Syntax: MKDIR directory 


Makes a subdirectory of the current directory. 
You can make directories on any drive of the PC, MC, HC or Series 3. 


You can make more than one subdirectory at a time - eg MKDIR \HOME\LETTERS makes the subdirectory 
\HOME\LETTERS and also the intermediate subdirectory \Home (if it does not exist). 


SSS SSS SS ee ee 
Changing MCLINK communications settings 
The set command in MCLINK allows you to: 

= use either of the PC's serial ports 

= change the Baud rate which the PC uses 

= use a modem (see later in this chapter for more details of using MCLINK over a modem) 


The set command creates a new MCLINK.TRM in the current directory to hold the new settings. The 
next time you run MCLINK from this directory, these settings will be loaded again. 


Options for the SET command 

Following the seT command you can specify a variety of options. For example: 
SET -pi -b9600 selects com1, 9600 Baud 

SET -p2 -b9600 selects com2, 9600 Baud 

The full range of options for the set command includes -p, -b, -m, -n, and -c. 


These options are also available as parameters to the command line for MCLINK (see later), but in this 
case, no permanent record of the options are made in any .7RM file. 


4 


1 MCLINK, MCPRINT, AND SLINK. 


Serial port and Baud rate options 


The SET options -p1 or -p2 select com1 or COM2. 


The option -b followed by a number sets the Baud rate. You will need to set the Baud rate on the MC, 
HC or Series 3 to match that on the PC. 


IMPORTANT: In general you should specify both -p and -b, or neither. If you specify just one, the other 
is reset to MCLINK's internal default value. The internal default for the Baud rate is 19200. (By default 
MCLINK runs at 9600 Baud since when starting up it looks for a file called MCLINK.TRM which is built 
in to MCLINK.EXE. This file sets the PC to COM1 at 9600 Baud.) 


Modem options 


The set options -m or -n specify that you are using a modem (as described in more detail later in this 
chapter). 


The option -m means wait for a call; MCLINK will establish the speed to use to the modem. 


The option -n followed by a number causes MCLINK to dial the number. The modem is assumed to 
conform to the Hayes command set. Here you can if you wish specify the speed for MCLINK to use, 
with -b. For example: -b2400 -n314159 dials 314159 at 2400 Baud. 


The option -c<string> will cause <string> to be transmitted to the modem to configure it before 
waiting/dialling: 


SET -cATMO turns off the modem speaker 
SET -cAT\N3 sets a Dacom modem to MNP fallback mode. 
Note that the at is optional in these commands. Multiple strings can be transmitted. For example, 


SET -cMO -c\N3 


Examples of the SET command: 


SET -b1200 1200 Baud 

SET -p2 -m waits for a call using the modem in port 2 (com2) 

SET -pi -b2400 -n314159 modem connected to port 1 dials the phone number 314159 
SET -cm0 turns off the modem speaker. 


SSS eS Sa ee 
Advanced use of MCLINK 


Running programs remotely on the MC, HC or Series 3 
The syntax 
RUN <program name>, <program command line> 
causes the named program to be nm on the remote computer, with the specified command line. 


The program is assumed to exist in the default directory of the remote machine or the root directory of 
any drive on the remote machine. Otherwise the ROM of the remote machine will be searched for the 
program. 


The program command line is as required by the program being invoked. 
For example, 

RUN CLOCK. IMG 
will run a copy of CLocK.1mMG on the remote machine. 


As an accelerator for running a copy of the remote shell program on HC machines the '!' command is 
equivalent to typing RUN SYSSSHLL. 


For example: 


! DIR 


ADDITIONAL SYSTEM INFORMATION 
————$ $e 


will run a copy of the sys$sHLL program passing an initial command line of DIR. See the HC 
Programming Guide for further information on commands available to the sYs$sHLL program. 


MCLINK batch files 

MCLINK can read a text file containing multiple commands: 
= Any line in the file containing a '!' is assumed to be a comment line and is ignored. 
# Any blank line is ignored. 


= You may specify almost any MCLINK command, although some may be meaningless in this 
environment - DIR, for example. 


= §=©The sET command should not be used. 


To invoke, a batch file, type the 'a' character immediately followed by the name of the text file 
containing the commands. For example, if the file SEND_ALL.TXT contains the following lines: 


! Sends all text files to the remote machine after 
! creating the correct directory, then exits 
MKDIR REM: :M:\NOTES\ 

COPY *.TXT REM: :M:\NOTES\ 

COPY *.DOC REM::M:\NOTES\ 

EXIT 


then entering @SEND_ALL.TXT at the '$' prompt will cause the \NoTEs\ directory to be created in the internal 
memory of the remote machine, all the .1xT and .poc files in the current directory to be copied to this 
directory, then MCLINK to exit. 


MCLINK command line processing 


MCLINK understands a command line entered when running MCLINK from the MS-DOS prompt. The 
command line may take the form of the parameters to the sET command, a filename assumed to be a 
configuration file, or the @ command to run a sequence of commands. 


Examples: 


MCLINK -p2 -b9600 will run MCLINK using port 2 at 9600 Baud. No .TRM file will 
be created, unlike using the set command, and any future running 
of the MCLINK program will not use these parameters. 


MCLINK S3SETUP will run MCLINK forcing it to use the file S3SETUP.TRM as the 
initial configuration file. This file would typically have been 
created by a previous use of the sET command within MCLINK. 


MCLINK @SEND_ALL.TXT will run MCLINK and cause the initial commands to be read 
from the file SEND_ALL.TXT. MCLINK will wait until a 
connection to the MC, HC or Series 3 has been established before 
running any of the commands. 

Invoking MCLINK inside an MS-DOS batch file 
Perhaps the most convenient way to automate a regular MCLINK task is via an MS-DOS batch file. 
For example, a batch file SEND_ALL.BAT could contain the single line 
MCLINK @SEND_ALL.TXT 
in which case the contents of SEND_ALL.TXT would be performed simply by typing 
SEND_ALL 


from the MS-DOS command line. 


ESS ES eee eee ee a oe eee ea 
MICLINK and modems 


A PC running MCLINK can use one modem at one end of a telephone line to connect to an HC or MC 
computer attached to another modem at the other end of the telephone line. 


1 MCLINK, MCPRINT, AND SLINK. 


Modem communication of this sort is possible only for MC and HC computers. The 3 Link software for 
the Series 3 does not have any built-in modem support for communicating over a telephone line to a PC 
running MCLINK. (The xp: device driver is not present in the ROM of the Series 3.) To communicate 
over a modem using a Series 3, use the Script language instead (which is included with the 3 Link 
software for the Series 3). 


MCLINK as a PC file server via the phone system 


When used as a PC file server via the phone system, MCLINK assumes that your modem follows CCITT 
tules and regulations concerning the RS232 signals - ie 


=" The modem drives DSR when powered up, never drops DSR and does not use DSR for any sort 
of handshaking. If the modem is physically removed, MCLINK detects this as the DSR signal 
disappears. If DSR is not driven MCLINK will not talk to the modem as it does not think it is 
there. 


« The modem responds to the DTR signal in the following manner - when MCLINK drives DTR 
low, (off, inactive) the modem should reset itself - ie disconnect if online etc and eventually 
enter its command mode. When MCLINK is exited, it drives DTR low to disconnect any calls 
currently connected. When MCLINK is started up it drives DTR low for 2 seconds to try to 
force the modem into its command mode, at its default settings. 


= The modem only drives DCD when it is on line to a remote modem, and drops DCD when the 
connection with the remote modem is lost. 


When MCLINK is asked to communicate with a modem it sends the 'aT' command string to the modem 
at the following Baud rates: 300, 600, 1200, 2400, 4800 and 9600. It monitors the response to sending this 
command, and sets itself to the highest speed at which an "ox" reply was received. 


MCLINK then sends the following command stream to the modem to configure it: 
"ATX4E0SO=1" if the modem's maximum Baud rate was 2400 Baud or above 
“ATX1EOSO=1" otherwise. 


MCLINK then reads the user command configuration strings passed to it and sends them to the modem. 
All commands sent to the modem are checked for validity by waiting for the modem to respond to the 
command sent. If the 'ok' response is received the command worked, otherwise an error is reported. This 
will result in MCLINK re-starting. 


NOTE: If the user command stream contains any form of reset, then the auto answering of calls should 
be re-enabled explicitly. 


MCLINK and modem Baud rates 


When MCLINK displays the status message Waiting for an Incoming Call the Baud rate displayed will be 
the fastest Baud rate that MCLINK found the modem supported. Setting the Baud rate is really a 
meaningless exercise since the Baud rate at which the modem connection is made is determined by the 
Baud rate of the dialling modem (originator) and not the modem accepting the call. 


If MCLINK detects the fastest Baud rate its modem can handle is 2400 Baud or above, it assumes that the 
modem has the ability to provide a constant speed interface - ie the modem does not change to the 
originator's Baud rate as soon as a connection is established. All modems that can handle Baud rates of 
2400 and above must have the constant speed interface since this is the way in which MNP throughput is 
normally achieved. 


If the fastest Baud rate is below 2400 Baud then MCLINK will set the connection Baud rate to that 
reported by the modem when it connects - ie the modem is assumed to change to the originators Baud 
rate and MCLINK will follow it. Modems in this class will not support any form of data 
compression/correction since a higher Baud rate than the connection Baud rate is required to achieve data 
compression. 


Link on the MC/HC as a requestor via modem 


(This section closely matches the corresponding section above for MCLINK.) 


When used on the HC or MC as a requestor via the phone system, Link software on the HC/MC assumes 
the modem follows CCITT rules and regulations concerning the RS232 signals - ie 


= The modem drives DSR when powered up, never drops DSR and does not use DSR for any sort 
of handshaking. If the modem is physically removed, Link detects this as the DSR signal 
disappears. If DSR is not driven Link will not talk to the modem as it does not think it is there. 


ADDITIONAL SYSTEM INFORMATION 


# The modem responds to the DTR signal in the following manner - when Link drives DTR low, 
(off, inactive) the modem should reset itself - ie disconnect if online etc and eventually enter its 
command mode. When Link is exited, it drives DTR low to disconnect any calls currently 
connected. When Link is started up it drives DTR low for 2 seconds to try to force the modem 
into its command mode, at its default settings. 


= The modem only drives DCD when it is on line to a remote modem, and drops DCD when the 
connection with the remote modem is lost. 


When Link is run it sends the 'at' command string to the modem at the following Baud rates: 300, 600, 
1200, 2400, 4800, and 9600. It monitors the response to sending this command, if it sees an 'oK' it assumes 
the modem can be driven at that Baud rate. 


Link then sends the following command stream to the modem to configure it: 
"ATX4E0SO=1" if the modem's maximum Baud rate was 2400 Baud or above 
"ATX 1E0SO=1" otherwise. 


Link then reads the user command configuration strings and sends them to the modem. All commands 
sent to the modem are checked for validity by waiting for the modem to respond to the command sent. If 
the ‘ox' response is received the command worked, otherwise an error is reported. This will result in 
Link re-starting. 

Link and modem Baud rates 


Link will send the dial string to the modem at the Baud rate specified from the Link dialog, or in the case 
of the HC at the Baud rate specified in the command line. If no Baud rate is specified, the fastest Baud 
rate that the modem responded to (see above) is used. By sending the dial string at the specified Baud rate 
the particular type of Vxx connection will be established - eg at 2400 a V22bis, at 1200 a V22, and at 
300 a V21 connection. Note that a V23 (1200/75) connection cannot be used. 


Note: if you want to use your HC as the file server and the PC as the requestor, swap the above 
instructions for 'MCLINK’ and 'Link'. 
Link/MCLINK with MNP 


If you have MNP modems do not be surprised if the data transfer rate between your PC and MC/HC is 
lower. This is because of the way MNP works. 


Typically it is not worth having MNP enabled. The protocol used by Link and MCLINK is based 
heavily on the MNP protocol, ie it provides an error free connection between the PC and HC. 


EES eee SS SS ee ee ee) 
Examples with modems 


The first four examples below focus on a PC with MCLINK as a file server. The last two examples focus 
on an HC with Link software. 


For more information on any of the configuration strings see the appropriate modem manuals. 


Using a Dacom QuadPlus MNP 5 compressing modem 

Run MCLINK with the following command line: 
MCLINK -C&F&C1&D3\N3\J1S0=1 

Alternatively, you could set up a .TRM file, eg QUADPLUS.TRM and type 
MCLINK @QUADPLUS.TRM 

or call the .TRM file MCLINK.TRM and just type 


MCLINK 


Using an Amstrad SM2400 modem 
Run MCLINK with the following command line: 


MCLINK -m 


1 MCLINK, MCPRINT, AND SLINK. 
a ee ee ee 


Using a Dowty Quattro SB2422 
Run MCLINK with the following command line: 


MCLINK -m 


Using a WorldPort 1200 pocket modem 
Run MCLINK with the following command line: 


MCLINK -m 


An HC with a Psion Quad modem 
Run Link with the following command line: 
LINK -n<the phone number> -c\n0 


This talks to all of the above PC file server configurations. 


An HC with the Amstrad SM2400 modem 
Run Link with the following command line: 


LINK -n<the phone number> 


Ee ee a ae ee a er eee 
Mecprint.exe 


MCPRINT allows an MC, HC, or Series 3 (or any other serial-printing device) to print to a printer 
which is connected to an IBM PC/XT/AT or compatible. The PC must have a free serial port which is 
used to connect to the MC/HC/Series 3. The printer may be connected to a parallel or serial port on the 
PC. 


Even if you can link the MC/HC/Series 3 directly to the printer, there may be reasons why it is more 
convenient to use MCPRINT: 


= The printer is shared by other PCs - either using a multi-port printer buffer or a local area 
network - and it would be unreasonable to connect it directly to the MC/HC/Series 3. 


= You do not wish to disturb the connection between the PC and the printer. 
= You have already set up the serial connection to use MCLINK for file transfer to the PC. 


= When your PC is connected to more than one printer, you can select which one the 
MC/HC/Series 3 will use. 


Note: it is also possible to print via MCLINK to a printer attached to your PC. To do this, set your 
MC/HC/Series 3 to print to a file, and give the printer device on REM:: (such as REM::LPT1) as the "file" to 
use. This method may be slower than using MCPRINT - especially when printing "justified" text from 
the Word Processor on either computer - and will only work reliably on version 3.0 or above of 
MCLINK. However, MCLINK can correct transmission errors, whereas MCPRINT can only report 
them. Such errors may occasionally be caused by PC add-ons, such as some network card drivers. 

Using MCPRINT 


Physically connect the MC/HC/Series 3 to the PC exactly as for MCLINK. If the printer is connected to 
LPT1, and MC/HC/Series 3 is connected to comi, you can now run MCPRINT on the PC by typing: 


MCPRINT 


If LpT1 and com! are not the ports used, parameters are required, as described below. 


Exiting MCPRINT 
To exit MCPRINT, press CONTROL-C. 


ADDITIONAL SYSTEM INFORMATION 


Printer configuration on the MC 


On the MC, select Print Setup from the Options menu in the System application to display the Printer 
dialog. Set one of the configurations to output to Serial. You don't normally have to click on the SET 
SERIAL... button to change any of the Serial options because MCPRINT uses the default settings. 


Once the current configuration has been set to Serial, you print as if the printer was directly connected to 
the MC - by selecting the Print menu item in any application which can print. As far as the MC software 
is concerned, it is printing to a Serial printer. 

Printer configuration on the Series 3 


On the Series 3, select the Printer setup option from the Special menu in the System screen. In this 
dialog, set the Printer device line to Serial. You don't normally have to change the Serial characteristics 
(it displays a subdialog when you press TAB) because MCPRINT uses the default settings. 


You can now print as if the printer was directly connected to the Series 3 - by selecting the Print option 
in any application which can print - including the built-in Word Processor, Agenda, Database, and 
Program editor. (Remember first to use the Print setup options in these applications, to tell the Series 3 
about the type of printer and the page layout desired.) As far as the Series 3 software is concerned, it is 
printing to a serial printer. 


Parameters 
MCPRINT takes the following parameters: 
<prdev> -c<port> -t<timeout> -q 
all of which are optional. To be reminded of these parameters, type: 


MCPRINT ? 


The <prdev> parameter 


<prdev> is the print device name, as for the MS-DOS print command. If omitted, the default is Lpt1 (the 
MS-DOS name for the first parallel port). 


For example, to print to LPT2, type: 
MCPRINT LPT2 


If the PC is a station on a local area network, LPT2, LPT3 etc may be used to redirect output to remote 
printers attached to the network server. 


The <prdev> parameter may specify any suitable output device. If you have a printer connected to a 
second serial port on the PC, you can use: 


MCPRINT COM2 


In this case, you should have previously used the MS-DOS mope command to set the serial parameters to 
be used between the PC and the printer. 


You can also print to a file on the PC using, for example: 
MCPRINT PRINT.LIS 
Note that PRINT.LIS will be overwritten each time you print. 
To test the connection without wasting paper, type: 
MCPRINT CON 


con is the MS-DOS name for the console (screen). When you then print from the MC/HC/Series 3, you 
should see the output appear on the screen of the PC. 


The -c<port> parameter 


<-c> is the serial port on the PC to which the MC/HC/Series 3 is connected. If omitted, the default is 
port 1, which corresponds to com1. If the MC/HC/Series 3 is connected to the PC's second serial port, 


type: 
MCPRINT -C2 


10 


1 MCLINK, MCPRINT, AND SLINK 


The -t<timeout> parameter 


<timeout> is a number of seconds. This parameter is provided for use on local area networks where it is 
necessary to close and open the print device between each print job. It is used to specify an inactivity 
time-out in seconds. If there is no printing for this period, the print device is automatically closed. For 
example: 


MCPRINT LPT2 -T5 
will close the print device after 5 seconds of inactivity. 


If the parameter is omitted, the print device is not automatically closed. You don't have to specify a time- 
out, as you can close the print device manually by pressing any key on the PC keyboard. Exiting 
MCPRINT will also close the print device. 


The MC/HC/Series 3 are multi-tasking - while one application is printing, you can carry on with 
something else. However, the background printing can be held up at times, depending on the processing 
requirements of the work you are doing. This can fool MCPRINT's inactivity time-out into thinking that 
the printing has finished, causing it to prematurely close the print device. If you experience this problem, 
consider increasing the time-out, or convert to closing the printer device manually by pressing a key on 
the PC keyboard. 

The -q parameter 


This parameter suppresses status messages (the 'q' stands for quiet"). 


SS SSS SS > ee ee, A) 
Slink.exe 


SLINK is a "no-frills" server-only version of MCLINK.EXE. However, it may run on “"PC"s which are 
less than 100% PC-compatible and cannot run MCLINK, and it may run in combination with other 
software which conflicts with MCLINK. 


By default, SLINK uses the com1 port, at 9600 Baud. You can specify on the command line the Baud rate 
and the serial port to use. These are in the same format as in the set command in MCLINK. For 
example: 


SLINK -p2 -b9600 


This sets SLINK to use com2. Note that, as with the sET command in MCLINK, -b9600 is used in this 
example to keep the Baud rate at 9600. 


If you just type SLINK -p2 this will reset the Baud rate to the internal default of 19200. 
Press Q to quit SLINK. 
SLINK has no support for modems. 


11 


CHAPTER 2 


RESOURCE FILES 


This chapter covers a range of related topics: 
= reasons programmers might consider using resource files 
= the format of Sibo .rsc resource files 
= the Olib rscfile class that can be used to read resource files 
# the Sibo resource compiler tool, rcomp.exe, that can be used to create resource files 
= general considerations about multi-lingual applications. 


Although the topics are all related, it is by no means necessary to read and understand all the sections in 
this chapter, just in order to understand one of these sections. 


aS Sa a en Sea ee EE Te 
Introduction 
There are two main reasons why a programmer may wish to use resource files: 


= Having data in a resource file, rather than as part of the program itself, cuts down on the size of 
the data segment required by the program, and thus makes more efficient use of RAM. 


= Resource files make it easier to write applications that can run in more than one language (eg 
English, French, German...). 


Text strings are a simple but important example of data that can be stored in a resource file. Suppose a 
program contains lines of code such as 


winfoMsg("Starting calculation"); 
and 

wSetBusyMsg("Scanning"); 
or even 

p_printf("%d items found",num); 


The dataspace of this program, when compiled and linked, would contain the three strings "starting 
calculation", "Scanning", and "%d items found" - a grand total of some 45 bytes (note that a terminating 
zero is stored for each string). A larger program may have many times this number of data strings; up to 
2k would not be uncommon. Now this data would be permanently loaded into RAM all the time the 
program is running. As a result, 2k less space would be available to the ordinary data of the program - 
such as cells in a spreadsheet, or text in a word processor - thus reducing the amount of such data that the 
program can accept before giving an “out of memory" error. Moreover, the operating system would be 
more likely to refuse to load and run the program, on account of insufficient memory being available to 
Start it. 


Next consider how the problem worsens for a program that is to be translated into more than one 
language. Either the program has to carry the data for all the different target languages, with a choice 
being made at run time between the various different possibilities: 


13 


ADDITIONAL SYSTEM INFORMATION 


if (language==LANG_ENGLISH) 
wSetBusyMsg("Scanning"); 

else if (language==LANG_FRENCH) 
wSetBusyMsg("'Parcourt"); 

else if (language==LANG_GERMAN) 
wSetBusyMsg("Suche") >: 


or else the code will have to be recompiled each time for a new language. But this latter approach makes 
the problem of maintaining code much harder; each different change made to the code, such as a bug fix, 
will have to be propagated to all the different language versions. At the same time, the job of the 
translator is not helped by the text to translate being all mixed up with the rest of the code, whilst if the 
translator works on a separate list of text strings, there is the risk of transcription errors when the 
separate lists are merged back into the code. 


For reasons such as these, serious programming in any system (Sibosdk or otherwise) frequently adopts 
one or other resource file approach for text strings and other data items. The strings "starting 
calculation", "Scanning", "%d items found", and so on, are kept in a separate file, not as part of the 
dataspace of the program, and are loaded into RAM only when they are needed. 


Thus the above call 
winfoMsg("Scanning"); 

would be replaced by a call such as 
InfoMsg(RESOURCE_SCANNING); 


where RESOURCE_SCANNING is a symbolic constant (#define) giving the index of the text string "Scanning" in 
the resource file. (The exact meaning of the index varies between different resource file schemes. See 
below for the meaning in Sibo resource files.) 


The contents of the routine InfoMsg would be something like 


LOCAL_C VOID InfoMsg(INT index) 
{ 
TEXT buf [60]; 


LoadResourceString(&buf [0] , index); 
wiInfoMsg(&buf [0] ); 
} 


and in turn LoadResourceString would read data from the appropriate resource file. 


At the initialisation of the program, the name of the appropriate resource file would be determined, once 
and for all, by reference to the current language (as obtained by a call to p_getlanguage). 


Some uses of resource files on Sibo computers 


Each of the built-in or bundled applications on the MC and Series3 ranges has its own resource file, in 
which are kept menu and dialog data, as well as more basic text strings. The dialog data can contain 
numerical layout information and numerical flags customising individual items within dialogs. 


Since the text strings and dialogs used by these different applications often overlap, there is also a so- 
called system resource file, where common items are kept. Thus an application loads data at various 
times from each of two different resource files - its own application resource file, and the system one. 


The .wdr printer driver files used by the printer subsystem in form.dyl (as on the Series3 - see the WDR 
Printing chapter in this manual for more details) are also resource files, with the data items consisting of 
escape sequences, font width tables, and other printer data. 


Finally, low-level error messages are defined in another file in the ROM, sys$ctry.cfo. This file also 
contains the keyboard layout tables, the fold tables, and other standard text such as the names of the days 
of the week. See the Config Files chapter for more details. 


14 


2 RESOURCE FILES 


(Se a ee a a ee ee i ee eer | 
Format of Sibo resource files 
There are in fact three kinds of Sibo resource files: 


a .cfo files, which are language configuration files (sometimes just called config files), with 
sys$ctry.cfo being the principle example 


® .rsc files, which are standard resource files 
=  .rzc files, which are Huffman compressed versions of .rsc files. 


Access to the data in .cfo files is via Plib functions such as p_errs, p_gettext, and p_nmmon, as described 
in the Plib Reference manual. 


Data in .rs¢ and .rzc files can be accessed using the functionality of the rscfile class in olib.dyl, as 
described later in this chapter. 


The Sibo resource compiler, rcomp.exe, can be used to create instances of .rsc files from plain text input 
known as resource scripts, which typically have extension .rss. This process is also described later in 
this chapter. However, the format of .rsc files (described immediately below) is so straightforward that 
programmers could easily create their own tools for producing customised .rsc files. 


Unless explicitly stated to the contrary below, the remainder of this chapter focuses exclusively on the 
.rsc type of resource files. 
The format of .rsc files 


A standard resource file just containing the three strings "starting calculation", "Scanning", and "%d 
items found", has the following contents (when dumped): 


O: 31 00 08 00 53 74 61 72 74 69 6e 67 20 63 61 &c 1...Star ting cal 
10: 63 75 6c 61 74 69 6f 6e 00 53 63 61 6e Ge 69 Ge culation .Scannin 
20: 67 00 25 64 20 69 74 65 6d 73 20 66 6f 75 be 64 g.%4d ite ms found 
30: 00 04 00 1900 220031 00  — ~— Jeeee ws 1s 


This conforms to the pattern: 


<header><resources><index table> 


where: 

<header> is always four bytes long, with the first word giving the file offset of the start 
of the index table, and the second word giving the length (in bytes) of the 
index table 

<index table> is a sequence of words, the first giving the file offset of the start of the first 
resource, the second giving the file offset of the start of the second resource, 
and so on, up to the last word, which gives the file offset of the end of the last 
resource (which is also the beginning of the index) 

<resources> are a series of variable length data items, whose contents can have any form. 


Some strategies for reading .rsc files 


Clearly, one way to implement a routine such as LoacResource is essentially as follows: 


GLDEF_C VOID LoadResource(UBYTE *pb,INT index) 
€ 
ULONG fpos; /* file offset */ 
UWORD tmp[2]; /* section of index table */ 


fpos=ixpos+(( index-1)*2);/* position into the index table */ 
p_seek(fcb,P_FA8S,&fpos); 

p_read(fcb, &tmp[0] ,4); /* read two words from index table */ 
fpos=tmp [0] ; 

p_seek(fcb,P_FABS,&fpos); 

p_read(fcb, pb, tmp[1]-tmp[0] ); 

> 


where: 


« feb is the file control block of the open resource file 


15 


ADDITIONAL SYSTEM INFORMATION 


= ~ixpos is the value of the first word in the resource file (ie the file offset of the beginning of the 
index table), and has been read into memory during program initialisation, for the sake of 
efficiency 


® the passed value of index in this case would be 1 for the first resource, 2 for the second resource, 
and so on 


# the code would need modifications to cope with possible error values returned by the p_seek or 
p_read calls (further discussed below). 


This strategy relies on a value of ixpos being stored in program memory. Another strategy would be to 
store the entire index table in memory - and this is the reason why the length of the index table is 
recorded as the second word in the resource file. However, in practice there is no observable speed 
degradation on account of reading index table data from the file every time a resource has to be loaded, 
and so the earlier scheme is generally to be preferred - in view of the lesser demand it places on RAM 
usage. 


Example of reading resource files directly 


See the file readrsc.c in \sibosdk\demo for an example of how to read the contents of a resource file 
directly (i.e. without using the services of the rscfile class). 


This example code assumes that the resource file is embedded in the application's program (.app) file. 
Thus, when used in an application, the name of the resource file should be specified on the second line of 
the application's .afl file. 


To use the example code, you must create a resource file with its first two resources both being short text 
strings. Any additional resources are not read by the supplied code. 


bn | 
Using the rscfile class in Olib 


Although it is possible, along the lines discussed above, to read .rsc resource files using ordinary Plib 
function calls, there are various reasons for instead using the functionality of the rscfite class in 
olib.dyl: 


= Using the rscfile class avoids needing to remember any details of the format of .rsc files 


= The rscfile class also contains considerable logic, hidden from the casual user, to decode .7zc 
Huffman compressed resource files - so that a decision can be taken at a later stage, to use .7zc 
format files instead of .rsc format, without any need to alter or recompile existing code 


= The rscfile class automatically takes care of locating resource files suitably embedded in a .img 
file - see below for more details 


= The interface to the rscfitle class clarifies and documents all the possible error conditions that 
need to be catered for 


= Learning about the rscfile class is a useful step along the route to learning about Psion's 
proprietary object-oriented programming system - since this system makes heavy use of the 
rscfile class. 


Basic services of the rscfile class 


Before the functionality of the rscfile class can be used in a program, an instance of this class needs to 
be created and initialised. This is dealt with below. 


The outcome of the initialisation is a handle, rather like a handle to a file control block or to other i/o 
device control blocks. Subsequent rscfite services are directed via this handle. 


The most primitive rscfile service is to load a resource, specified by index number (starting at 1 for the 
first resource), into a supplied buffer. This is the rs_read_buf service. In this case, it is assumed that the 
caller has supplied a sufficiently long buffer. 


Sometimes, however, it is more appropriate for the rscfile class to allocate a cell of sufficient length, for 
the resource to be loaded into. This typically applies when a resource can have variable length, and 
when the resultant alloc cell will have some permanence. The rs_read service fulfils this requirement. 


16 


2 RESOURCE FILES 


As an example of the rs_read_buf service, consider the following routine InfoMss: 


LOCAL_C VOID InfoMsg(INT index) 
{ 
TEXT buf £60] ; 


p_send4(rcb,0_RS_READ_BUF, index, &buf [0] ); 
wInfoMsg(&buf [0] ); 
> 


with reb being the handle of a suitably initialised rscfile object. 


As an example of the rs_read service, consider loading some menubar data in from a resource file. For 
Hwif programs, this data is (in part) in the form of an H_MENU_DATA struct. The address of this struct has 
to be written to the static _mdata (of type H_MENU_DATA*) whose existence the Hwif library presupposes. In 
that case, the following call might be made during the initialisation of an Hwif application: 


p_send4(reb,O_RS_READ,MENU_DATA_INDEX,& mdata); 
where MENU_DATA_INDEX is a symbolic constant giving the index of the appropriate resource. 


Note that although the calling interface to rs_read_buf and rs_read may look similar, they require 
different types for the penultimate parameter: 


= rs_read_buf requires a parameter such as a TEXT* or a UBYTE*, ie with one level of indirection 
from the actual loaded data 


=  rs_read requires a parameter such as a TEXT** or a UBYTE**, ie with two levels of indirection from 
the actual loaded data. 


Barring run-time errors (discussed below), the calls rs_read_buf and rs_read both return the length of the 
resource read. Note that in the case of a (zero-terminated) string, this length includes the length of the 
terminating zero, since that is part of the resource too. 


Reading compressed resource files with the rscfile class 


If a Huffman compressed resource file, typically with extension .rzc, is substituted for a standard 
resource file (typically having extension .7sc), there is no need to alter or recompile in any way 
application code making use of the rscfile class. The interface remains exactly the same. 


The only point possibly worth mentioning is that the lengths returned by rs_read and rs_read_buf are the 
length of the resources once decompressed, and not the length of the compressed resources on file. 


Initialising an rscfile object 


The following code can be used to create and initialise an rscfile object providing access to a resource 
file with name rscname (assumed to be a full path name): 


VOID *InitReb(TEXT *rscname) 
{ 
HANDLE OlibCat; 
VOID *reb; 
INT ret; 


p_findtib("OLI8.DYL",&0libCat); 
rcb=p_newlibh(Ol ibCat,C_RSCFILE); 
if (reb) 
{ 
ret=p_entersend3(rcb,O_RS_INIT,rscname); 
if Cret<0) 
p_exit(ret); 
D 
return(rcb); 
> 


For overtly object oriented programs, the lines 


p_findlib¢"OLIB.DYL",&0libCat); 
reb=p_newlibh(OlibCat,C_RSCFILE); 


17 


ADDITIONAL SYSTEM INFORMATION 


can be replaced by a line such as 
reb=p_new(CAT_HWIF_OLIB,C_RSCFILE); 


with the category number CAT_HWIF_OLIB being replaced by the suitable reference to olib.dyl from the 
native category. 


For Hwif programs, the line 
rcb=p_new(1,C_RSCFILE); 
can be used instead, taking advantage of the fact that the value of the (normally hidden) symbolic 
constant CAT_HWIF_OLIB is 1. 
Which header files are needed 


The symbolic constants C_RSCFILE, O_RS_READ, O_RS_READ_BUF, and O_RS_INIT, are defined in the object- 
oriented include file appman.g. 


Duplicates of these definitions are given in the special SDK file rscfile.xg. 


The value of o_pEstroy (defined in olib.g) is 0. (See below for use of o_DESTROY.) 


Run-time errors with the rscfile class 

Broadly speaking, there are five kinds of run-time error that can arise with resource files: 
there is insufficient memory to create or initialise the rscfile object 

the resource file cannot be found (when the program starts) 

the data in the resource file is bad 


the SSD containing the resource file is removed or cannot be accessed 


vA F&F WwW NY & 


there is insufficient memory to load a specified resource. 


Of these possibilities, the third is regarded simply as a programming error. Thus if some data in what 
should be the index table part of the file effectively says that a certain resource has length 5398 bytes, 
whereas the file itself is smaller than this size, the rscfile object will panic the application (with panic 
number 141). 


Otherwise, errors 1 and 2 can occur when initialising a rscfile object, whereas error 4 and 5 can occur 
when subsequently using the object. 


Possible errors during initialisation 


The only reason the calls p_newl ibh or p_new in the above routine InitReb will fail is on account of lack of 
memory. Applications can choose to discount this possibility if their minimum heap is appropriately 
calibrated - see below. 


The call to rs_init can fail with file-based errors on account of the filename in *rscname. The most 
pertinent possibility (assuming that a well-formed name has been passed) is that the specified file does 
not exist. Applications could guard against this by checking on the existence of the file prior to calling 
InitReb. If the file does not exist, the user can be notified, and the program exited. 


Errors during rs_read or rs_read_buf 


The only errors that an application should in practice worry about, for the rs_read and rs_read_buf 
services, are the fourth and fifth in the above list. 


Running out of memory can occur only in the case of rs_read - when it is impossible to allocate a call 
from the heap large enough to load the resource into. The implementation of rs_read_buf is guaranteed 
never to fail with out of memory. 


If calls to rs_read are made during program initialisation only, it may well be legitimate to ignore the 
possibility of out of memory errors in this case too - provided the declared minimum heap of the 
application is large enough. The point is that only memory in the dataspace of the application has to be 
allocated - not any memory in another process such as the File Server (see the chapter Fundamental 
Programming Guidelines in the General Programming Manual for related discussion). 


However, applications calling either rs_read or rs_read_buf ought always to consider the possibility of 
the user removing the SSD containing the resource file. What will happen in this case is as follows: 


18 


2 RESOURCE FILES 


= — suppose the user removes the relevant SSD, not realising (or forgetting) that the program may 
wish to access data on it 


= the application makes a call to rs_read or rs_read_buf 


= system code, detecting that the file is missing, presents a Notifier requesting the user to replace 
the SSD; this Notifier has two exit options: Retry and Fail 


= the user ought to replace the SSD and select Retry; however, it is possible that the Fail option 
will be selected 


® in this case, an error such as E_FILE_ABORT will be generated. 


How rscfile errors are reported 


The rscfile services rs_read and rs_read_buf do not return any error values; instead, they internally call 
p_leave. 


Applications performing sophisticated error handling, using p_enter, should be sure that p send calls to 
rs_read OF rs_read_buf are (ultimately) enclosed in some call to p_enter - otherwise any errors will result 
in their application being panicked, with panic number 47. 


Applications not wishing to use p_enter should replace the above calls to p_send with calls to 
p_entersend, as follows: 


ret=p_entersend4(rcb,O_RS_ READ BUF, index, &buf [0] ); 
and 
ret=p_entersend4(rcb,0_RS_READ,MENU_DATA_INDEX,& mdata); 
Possible values of ret that can be returned are as follows: 
positive value the length of the resource loaded (no error has occurred) 


negative value an error has occurred: either E_GEN_NOMEMORY for out of memory, or some other 
value in case the resource file could not be accessed. 


Note incidentally that the complications over possible errors while reading resource files are by no means 
exclusive to the .7sc format of resource files. An application could devise its own format of data file, 

and its own library of routines to extract data from these files, but these routines would have to cope with 
all the same error possibilities as for the rscfite routines. That is, the error possibilities stem not from 
the rscfile class, but from the notion of resource files itself. 


Dealing with errors in rs_read or rs_read_buf 


(This section should be skipped on a first reading.) 


There follows a more detailed example of how to deal with possible errors during an rs_read call. The 
case for rs_read_buf is similar, albeit simpler (since there is no possibility of an out of memory error in 
this case). 


19 


ADDITIONAL SYSTEM INFORMATION 


LOCAL_C VOID ReadResource(VOID *ppcell,INT index) 


€ 
INT ret; 
FOREVER 
{ 
ret=p_entersend4(rcb,O_RS_READ, index, ppcell); 
if (ret>=0) 
return; 
while (ret<0) 
¢ 
if (ret==E_GEN_NOMEMORY) 
{ 
*ppcell=NULL;/* signal failure to caller */ 
Tel LNoMemory( ); 
return; 
> 
wsAlertW(WS_ALERT_CLIENT,0O,ReplaceDisk,0); 
p_send2(rcb,0_DESTROY); 
FOREVER 
€ 
reb=p_newlibh(OlibCat,C_RSCFILE); 
if (reb) 
break; 
Tel LNoMemory(); 
> 
ret=p_entersend3(rcb,O RS_INIT,rscname); 
> 
} 
> 


One way to implement the routine Tel \NoMemory - which must never itself run out of memory - would be 
as follows: 


LOCAL_C VOID Tel lNoMemory(VOID) 
€ 
TEXT buf £40]; 


p_errs(&buf [0] ,£_GEN_NOMEMORY ); 
wsALertW(WS_ALERT_CLIENT,0,&buf[0} ,0); 
> 


The way ReadResource works, in cases when the user has removed the SSD and has refused to replace it, 
is to present another alert, wait for the user to respond (by pressing ESC), and then try to re-make the 
connection with the resource file. For this purpose, various statics are accessed: 


OlibCat The value of the category handle of olib.dyl, as returned by the earlier call to 
p_findlib 

rscname The full path name of where the resource file should be 

ReplaceDisk Text that might read, in English, "Replace the application disk”. 


Clearly, for multi-lingual applications, the string ReplaceDisk must itself be read from a resource file. 
Since the channel to the application resource file is broken at this stage, it may be necessary to read in 
this text during program initialisation. 


Standard practice for applications on a Series3 is to bracket calls to wsAlertw with increments and 
decrements to the reserved static DatLocked: 


DatLocked++; 
wsAlertW(...); 
DatLocked--; 


Incidentally, the error handling mechanism described above is implemented automatically for object 
oriented programmers who use the appman class and its methods am_load_resource and am_res_buf to read 
data from resource files (except that the error recovery code in appman is even better, in that it caters with 
the case of the SSD being removed from one drive and replaced in another). 


20 


2 RESOURCE FILES 


SSS ee ee ee 
Advice on where to locate resource files 


Mono-lingual applications 


In the case of a mono-lingual application, the safest place to locate a resource file is within the image file. 
The rscfile class will find any resource file in the second of the four possible add-file slots in an image 
file. 


For example, if the application is called archive.app and the resource file is called archive.rsc, an add- 
file list archive.afl should be created, with the following contents: 


archive.pic 
archive.rsc 


where archive.pic will be placed in add-file slot 1, and archive.rsc in add-file slot 2. 


The named files will be added to the resultant image file, whenever this is made, just by virtue of the 
existence of an .afl file with the same basic name as the image file. 


In this case, the appropriate name to pass to rs_init is simply DatCommandPtr (recall that a zero-terminated 
string giving the full path name of the image file is placed at this reserved static, by the operating system, 
when the process is started): 


p_entersend3(rcb,O_RS_INIT,DatCommandPtr); 


Not only does this scheme have the advantage of simplicity, it also prevents accidents if users copy the 
main image file to an SSD, but neglect to copy the associated resource file. 


For a multi-lingual application, the above continues to apply in any case when a different .img file is re- 
made (using the tool eremake) for each new language version. (For more details about eremake, see the 
chapter Building an Application in the General Programming Manual.) 


However, for multi-lingual applications in which the resource data for more than one language is shipped 
together, the resource files must in general be separate from the main .img file. (There is no scope for an 
indefinite number of add-files.) The documentation for the application should emphasise to users that if 
the .app file (or the .img file) is copied from one SSD to another, for consolidation purposes, then 
appropriate .rsc (or .7zc) files should also be copied. 


Multi-lingual applications 


One scheme that has much to recommend it is to rename the resource files as follows: 


archivO2.rsc for a French language resource file 
archiv03.rsc for a German language resource file 
archiv18.rsc for a Dutch language resource file 


and so on (for an application archive.app), where the numbers at the end of the filename are the language 
codes of the target languages, listed in the documentation of p_get language in the Plib Reference manual. 


These files should be located in a sub-directory underneath the directory containing the application 
program file. The name of this subdirectory should be the same as the basic name of the program file. 
For example, if the full pathname of the program file is \app\archive.app, the full pathnames of the 
resource files should be \app\archive\archiv??.rsc. Again, resource files used by a program with full 
pathname \imng\backup.img should be located as \img\backup\backup??.rsc. 


Then code to determine the name of the resource file, suitable to the language of the computer at run 
time, could be as follows: 


21 


ADDITIONAL SYSTEM INFORMATION 


TEXT *FindRscName(VOID) 
€ 
LOCAL_D TEXT RscNameBuf [P_FNAMESIZE]; 
TEXT *RscName; 
P_INFO f; 


RscName=(&RscNameBuf (0) ); 
p_atos(RscName,"\\app\\archive\\archivz0ed.rsc",p getlanguage()); 
p_fparse(RscName, DatCommandPtr ,RscName,NULL); 
if (p_finfo(RscName,&f)<0) 

p_scpy(RscName,DatCommandPtr); 
return(RscName); 
> 


Note the check on the existence of the first filename generated by this routine; in case the language as 
returned by p_getlanguage is not supported by the application, the routine defaults back to whatever 
resource file is built into the application. 


Copying of applications 


The above recommendation for where resource files should be located conforms to the important general 
rule that if users wish to copy an application \path\name.ext from one SSD to another (say from drive a: 
to drive b-), all they need to do is type 


copy a:\path\name.ext b:\path\name. ext 
copy a:\path\name\*.* b:\path\name\*.* 


in which case (assuming the application is not copy-protected!) all the files required or presupposed by 
the application will be transferred. 


ee ee es ae ee ee a en eee 
General comments on multi-lingual applications 


It is a common programming error to design an application too closely around the text of one language 
(eg English), and to discover only at some late stage that various assumptions made fail when the 
application is translated into another language. 


For example, if the English text "Weekly" is to be read into some resource, it may be tempting to write 
some code as follows: 


TEXT buf [8]; 


p_entersend4(rcb,O_RS_READ_BUF ,WEEKLY_INDEX,&buf (01); 


However, when the application is translated into German (say), with the entry for "Weekly" in the 
resource file being changed into "Wéchentlich", the new application will most likely crash when the above 
code is run. The reason is that the buffer of eight bytes, which was long enough to contain the text 
"Weekly", is not long enough to contain "Wéchentlich". 


Better therefore to decide in advance what a reasonable limit on the translation of this term should be, 
and use that as the size of the buffer in the code (not forgetting to inform the translators what the limit 
is). 


The basic principle of independence of code from resource file 


One basic guideline is that the code itself should not have to be altered, just because a new translation has 
been undertaken. The original code should be general enough to start with. 


The main problem with allowing code to change at a later date, to simplify the task of translators, is that 
it is often difficult to foresee the side-effects of such a change. In practice, the most intense testing an 
application receives is just prior to its launch in the original language; if changes are made at a later date, 
these may introduce bugs which slip through subsequent testing, on account of that testing being less 
severe. 


For this approach to work, a special test plan has to be devised, focussing on the purely language 
dependent parts of the application. This test plan should include means of loading, one by one, ail the 
resources from the resource file, and displaying them on the screen for validation. 


22 


2 RESOURCE FILES 


Provided changes made in response to problems thrown up by this test plan are restricted to the resource 
files themselves, there can be some confidence that the original intensive testing still holds good. But if 
code has to be changed, there is the risk of regression - something that used to work now no longer 
works. 


Careful design of screen layout 


Design of screen layout is another area where things can go unexpectedly wrong when the contents of a 
resource file is changed. 


In some cases, screen layouts which (just) work in one language, become untenable in another language, 
because there simply is no acceptable way of translating the text on the screen and still fitting within the 
allowed display area. For this reason, displays which are already cramped in the original language 
should be avoided: if they are cramped in one language, they will likely become "grid locked" in some 
other language, with slightly longer words. 


Even if there is ample room in some screen display, care should be taken to calculate various dimensions 
dynamically, ie at run-time, using the widths of the actual characters used, rather than statically (ie at 
compile-time). 


Codesize problems 


For related reasons, an application which, together with its resource file, only just fits on an SSD of a 
certain size (say 128k), will be unlikely to fit on a similarly-sized SSD when the resource file has been 
translated into another language. 


Of course, as with the other problems above, it is always possible to insist that a sufficiently brief 
translation be found, but this can result in abbreviations the user is likely to consider ridiculous. It is far 
better to include some “spare” in the original budget, to allow for some measure of growth as the 
translation takes place. 


Varying keyboards 


As well as the text of messages varying from one language to another, it is also possible for the keyboard 
layout to alter. This does not just mean changing from qwerTy to AZERTY, but changes in which characters 
can be typed in combination with various modifiers. 


For example, an application in one translation may define the hot-key PSION +/ as the accelerator for 
some menu command. However, a foreign language keyboard may move the / key into a place where it 
cannot be pressed in conjunction with the PSION modifier. Thus on the Series3 keyboard, PSION together 
with some keys changes the characters delivered, into altogether different ones. Therefore, the 
accelerator would have to alter to some other keypress. 


Something else that may have to change, on account of the keyboard changing, is references to the 
keyboard within eg Help text (or inside "Action buttons"). For example, the DELETE key may become 
the EFF key in a different language variant. 


Conclusion 


The possible problems of multi-lingual code form another item in the list of things that need to be 
constantly under background consideration as an application is written. 


The discipline of separating text into a resource file is a vital step in the right direction, but by itself, it 
does not guarantee that all related problems will be solved. Every time text is used in an application, the 
writer must ask the question: not what is the length of this text, but what might the length of this text be 
in some foreign translation - and what would the consequences of that be? 


LSS LSS ee a | 
Creating .rsc files using rcomp.exe 


The resource compiler is a tool rcomp.exe that operates on a so-called resource script, which is a text 
file, to produce a resource file as output. 


The process is akin to ordinary compilation: 
*.c + compiler -> *.obj 
*.rss + resource compiler -> *.rsc 


with .rss being the usual extension for a resource script. 


23 


ADDITIONAL SYSTEM INFORMATION 


As an example, suppose a file eg.rss has the following contents: 


STRUCT STRING 
€ 
TEXT str; /* zero terminated text string */ 
> 


RESOURCE STRING res_start_cale {str="Starting calculation";} 
RESOURCE STRING res_scanning {str="'Scanning";} 
RESOURCE STRING res_items_found {str=""%d items found"';} 


Then invoking the command line 
rcomp eg 


produces as output a file eg.7sc whose contents are exactly as described in the earlier section on the 
format of .7sc files. 


Note: some earlier versions of rcomp.exe do not accept the syntax 
RESOURCE <struct-name> <identifier> <definition> 
instead requiring the addition of the keyword GLOBAL: 


GLOBAL RESOURCE <struct-name> <identifier> <definition> 


Generated .rsg files 


As well as producing a .rsc resource file, running rcomp.exe also has the effect of creating a generated 
header file, with extension .rsg. 


Thus the output of typing rcomp eg is not only the file eg.rsc but also the file eg.rsg, having the 
following contents: 


#define RES_START_CALC 1 
#define RES_SCANNING 2 
#define RES_ITEMS FOUND 3 


In turn, C source files that need to specify resource indices ought to #inctude these generated .rsg files, 
so that they can include code such as 


InfoMsg(RES_SCANNING); 


The present value of RES_SCANNING is 2. Suppose however that a new resource is added at the beginning 
of eg.rss. This means that the resource "Scanning" is of course no longer the second in the resultant .7sc 
file, but the third. Accordingly, any calls such as 


InfoMsg(2); 

have to change into calls such as 
InfoMsg(3); 

in order that they have the same effect as before. Note that having these lines of code instead as 
InfoMsg(RES_SCANNING); 


and recompiling the C source files after changing the resource script automatically ensures that the 
desired outcome transpires. 


The syntax of the rcomp command 
The syntax for invoking rcomp.exe includes: 


rcomp [-s]<name> [-o<oname> -h<hname>] 


where 

<name> is the name of the resource script 
<oname> is the name of the resource file output 
<hname> is the name of the generated header file. 


One possible use of the fuller syntax is to redirect the generated header file to an .. \include\ directory. 


24 


2 RESOURCE FILES 
ee SS Eee 


Include files within a resource script 
A resource file can contain lines such as 
#include “archive.dh" 
or 
#include <archive.rh> 


(no particular significance should be attached to the extensions used in these examples). As would be 
expected, files specified using the quote form of #include are expected to be found in the local directory. 
However, files specified using the angle bracket form of #include are expected to be found in the 
directory (if any) specified by the value of the DOS environment variable INCLUDE. 


If required, a batch file such as follows could be used to invoke rcomp..exe: 


set OINCLUDE=%INCLUDEX 
set INCLUDE=..\include 
\sibosdk\sys\rcomp %1 
set INCLUDE=ZOINCLUDEX 
set OINCLUDE= 


preserving any previous value of INCLUDE for other purposes that may apply on a PC. 


Conditional compilation in resource files 
Note that the resource compiler supports conditional compilation such as 


#1 fdef BUILD_ONE 


#endif 
and 


#ifndef BUILD_ONE 


#endi f 


Names (such as BUILD_ONE) may be defined when invoking the resource compiler using a -d flag as 
follows: 


rcomp resfile -dBUILD_ONE 
This has the same effect as including the corresponding #define in the resource file, for example: 


#define BUILD_ONE 


ESE ———— ee a ee ae el 
Contents of .rss files 
Resource scripts (together with other files they #include) are made up of three types of statement: 
= comments (identified by C-style /* and */ delimiters) 
« declarations of structs and constants 
= declarations of REsourcEs, which are instances of the structs defined. 
All the declarations of structs have to precede the first definition of a RESOURCE. 


White space is ignored (after the first white space character), except within quoted strings. Thus the 
definitions 


RESOURCE STRING res_start_cale {str="Starting calculation";} 
and 


RESOURCE STRING res_start_calc 
€ 
str="Starting calculation"; 
> 


25 


ADDITIONAL SYSTEM INFORMATION 


are equivalent. Any indentation of source lines in resource scripts is purely for convenience. 


Declaring STRUCTs 


STRUCTS are formed of a name and a series of member definitions. stRucT names must always be given in 
upper case, whereas member names are given in lower case. For example, 


STRUCT STRING 
€ 
TEXT str; 
> 


defines a STRUCT with name STRING and just one member, which has type TEXT and member name str. 
Again, the definition 


STRUCT MENU_BAR_ITEM 
{ 
LINK menu_id; 
TEXT mb_item; 
} 


defines a struCT with name MENU_8AR_ITEM and with two members, the first with type LINK and the second 
with type TEXT. 


As is discussed below, member definitions can also include default initialisations. 


Possible member types in STRUCTs 
The set of allowed struct member types is as follows: 


BYTE Stores a numerical value in one byte 

WORD stores a numerical value in two bytes 

LONG stores a numerical value in four bytes 

DOUBLE stores a floating point numerical value in eight bytes 

TEXT stores a zero-terminated sequence of bytes, including the terminating zero 
LINK stores a two-byte reference to another RESOURCE 

STRUCT Stores a sub-STRUCT in-line. 


Of these types, only Text and struct have variable length (see below for more on variable length items). 
worDs are stored low byte first then high byte. Similarly, LonGs are stored low word first, then high word. 


The resource compiler accepts any value from -128 to +255 for a BYTE, so that C programs are free to 
interpret the contents as either signed or unsigned. worD and Lonc can similarly be interpreted either as 
signed or unsigned. 


The resource compiler can undertake some limited arithmetical evaluation of data supplied in numeric 
fields (eg val=4*3.18). 


TEXT data can be entered as a combination of quoted strings, binary values, and symbolic constants: 
str="This is a string."; 

or 
str=<84><104>"is a str’<0x69>"ng"<46>; 

or even 


#def ine DOUBLE QUOTE <34> 


str="Missing "DOUBLE_QUOTE; 


Standard C-style processing of backslashes applies within quoted strings. Thus to enter a single 
backslash in a string, the backslash character has to be repeated in the input, and so on. Thus the final 
example above could also be given as 


str="Missing \""; 


26 


2 RESOURCE FILES 


Allowed values of LINK items are constants, symbolic constants, or (lower-case) identifiers of other 
resources. In referring to another resource, both forward and backward references are possible. 
Declaring RESOURCEs 
A RESOURCE is declared by specifying: 

= the name of the struct being instanced 

a the identifier of the RESOURCE 

= = initialisers: values of all the members of the struct. 


The name of the struct must always be given in upper case, whereas the identifier of the RESOURCE must 
always be given in lower case. 


For example: 


RESOURCE MENU_BAR_ITEM file_mbar_item 
€ 
menu_id=file_menu; 
mb_item="File"; 
> 


During resource compilation, the various identifiers encountered are assigned the values 1, 2, 3, .... 


There is no requirement to list the members of the struct in the same order as their definition. Nor is it 
always necessary to give values for every member: 


® any member omitted will be given the default value supplied for that member, in the declaration 
of the struct, if any 


® failing this, a "default default” value of zero will be supplied 
s however, it is an error to omit altogether to give a value for a LINK member. 
For example, it is possible to declare an instance of 


STRUCT NCEDIT 
{ 
WORD current; 
WORD low; 
WORD high=65535; 
> 


just by the line 
RESOURCE NCEDIT ne_edit { } 


in a resource script, in which case a 6-byte long resource will be created, with the three consecutive 
words containing the values 0, 0, and 65535. 


Again, the result of resource compiling the following: 


STRUCT TEST 
€ 
TEXT str; 
BYTE byt; 
STRUCT more; 
> 


RESOURCE STRUCT TEST empty € > 


is a 2-byte long resource, with each byte being set to zero. (Note that "zero" sub-sTRUCTS are omitted in 
their entirety, ie taking up zero length in the resource file.) 


Declaring the values of sub-STRUCTs 
Whereas the way to define the value of most members of REsourcEs is by a statement in the form 


<member-name> = <constant>; 


27 


ADDITIONAL SYSTEM INFORMATION 


the way to define the value of a sub-struct member is 
<member-name> = <struct-name> {<initialisations>}; 


with the form of <initialisations>, if present, matching that of the definition of a REsouRcE itself. For 
example: 


STRUCT NCEDIT 
€ 
WORD current; 
WORD low; 
WORD high=65535; 
> 


STRUCT TEST 
€ 
TEXT str; 
BYTE byt; 
STRUCT more; 
> 


RESOURCE TEST values 
€ 
str="This is a string"; 
byt=42; 
more=NCEDIT {current=100;}; 
> 


In the majority of cases, when a sTrucT is declared as having a sub-sTRUCT member, this member will be 
intended to be a strucT of one particular type. Note, however, that the type of the sub-strucT member is 
not specified by the struct in which it is declared (for example, Test does not specify that the sub-sTRuCT 
more is of type NceDIT). In consequence, it is possible to have two or more resources that are both based 
on the same struct definition, but use different types of sub-struct. 


Leading byte and word length values 


In cases when sTrUCTs contain sub-sTructs, it is frequently helpful to have the instance of the sub-sTRUCT 
preceded by a byte or word giving the length of the instance. This is achieved by amending the 
definition of the sub-struct: the keyword BYTE or WORD should be included before the opening curly 
bracket prior to the definitions of the members of the struct. 


For example, resource compiling 


STRUCT FIRST BYTE 
€ 
BYTE one; 
> 


STRUCT SECOND WORD 
€ 
BYTE two; 
> 


STRUCT THIRD 
€ 
BYTE three; 
> 


STRUCT FOURTH BYTE 
{ 
STRUCT a; 
STRUCT b; 
STRUCT c; 
} 


RESOURCE FOURTH test 
€ 
a=FIRST {one=1;); 
b=SECOND ({two=2;}; 
c=THIRD {three=3;); 
> 


28 


2 RESOURCE FILES 


results in the following 6-byte long resource: 
<01><01><01><00><02><03> 


in which the first byte gives the length of the following sub-resource, the third and fourth bytes together 
constitute a word giving the length of the second sub-resource, and the final sub-resource has no 
preceding byte- or word- length value. 


Note that the resource as a whole lacks a leading byte- or word- length value, despite the presence of the 
BYTE qualifier in the definition of FourtH. These leading length values are inserted only when the instance 
of the STRUCT is as a sub-resource. 


Incidentally, it is common for definitions such as 


STRUCT FIRST BYTE 
€ 
BYTE one; 
> 


to be given instead in the equivalent form 


STRUCT FIRST 
BYTE ¢ 
BYTE one; 
> 
Arrays within resource files 


The resource compiler is at perhaps its most powerful in dealing with arrays of resources - strictly 
speaking, arrays of sub-resources. 


In order to declare an array of sub-resources, a member definition in a struct definition such as 
<type> <member-name>; 
has to be changed into one of the forms 
<type> <member-name> [<array-size>]; 
<type> <member-name>[ J; 
LEN <type> <member-name>{ 1; 
or 
LEN BYTE <type> <member-name> [ 1; 
For example, 


STRUCT HELP_ARRAY 
{ 
LINK topic_id=0; 
TEXT topic; 
LEN BYTE STRUCT strist{); 
} 


in which the initial LINK and TEXT sub-resources are followed by a variable number of sub-structs (the 
number varying between different instances of HELP_ARRAY). 


In all cases with arrays, the corresponding initialiser statement in a RESOURCE definition 
<member-name> = <constant>; 

changes into the form 
<member-name> = { <constant>, <constant>, ..., <constant> }; 


with <constant> being replaced by <struct-name> {<initialisations>} in the case of an array of sub- 
STRUCTS. 


For a variable sized array, the array of sub-resources may be preceded in the resource file by a byte or 
word giving the number of elements actually in the array. This count is recorded in a word if the prefix 
LEN is used, and in a byte if the prefix LEN BYTE is used. 


29 


ADDITIONAL SYSTEM INFORMATION 


Evidently, the number of sub-resources that actually occur in any given instance of the struct is 
determined by the syntax of the intialiser, with all but the last element being preceded by a comma. 


For example, resource compiling 


STRUCT STRING 
€ 
TEXT str; 
} 


STRUCT HELP_ARRAY 
{ 
LINK topic_id=0; 
TEXT topic; 
LEN BYTE STRUCT strist{(]; 
> 


RESOURCE HELP_ARRAY sys_help print 

€ 

topic="How to print"; 

strist= 
€ 
STRING {str="Set Printer Model with ‘Print setup'";}, 
STRING {str="in Word/Agenda/Data, then use 'Print!";} 
7 

> 


produces a single resource in which: 
= the first word is zero (the supplied default value for the topic_id member) 
® then there follows the sub-resource "How to print" 
= next comes a byte containing the value 2, being the count of the items in the following array 


® finally the strings "Set Printer ..." and "...Print'™ are juxtaposed. 


Creating SYSTEM resource files 


For completeness, it should be mentioned that the resource compiler enters a special mode if the first line 
of a resource script is found to consist of precisely the single word 


SYSTEM 
In this mode, all LINK references are automatically resolved with the negative of the correct value. 
Thus whereas the resource script 


STRUCT STRING {TEXT str;} 
STRUCT TEST {LINK Lnk;}> 


RESOURCE STRING alpha {str="Xyz";} 
RESOURCE TEST beta {Lnk=alpha;}> 


results in the second resource consisting of the word 1, inserting a line 
SYSTEM 
at the beginning of the resource script changes the value of this resource to -1. 


The rationale of this behaviour is connected with the facility offered to object-oriented programmers by 
the appman class in olib.dyl, to load in resources from the so-called system resource file instead of from an 
application-specific resource file, if the resource identifiers passed are negative. This system resource file 
is built into the ROM of all machines that support HWIM programming. See the Resource Files chapter 
of the Object Oriented Programming Guide and the APPMAN Application Manager Class chapter of the 
OLIB Reference manual for more details. 


30 


CHAPTER 3 


WDR PRINTING 


Introduction 


SIBO computers which contain form.dyl in their ROM (or on an SSD) support a wide range of services 
connected with so-called WDR printing. These services include the interpretation of printer driver files 
in the Psion-proprietary .wdr format, and the generation of suitable printer command sequences to effect 
printing operations requested by applications. One key idea is that applications do not need to know 
which particular printer driver has been selected by the user; system software takes care of converting 
requests made by applications into command sequences suited to the current printer. 


Another service which code in form.dyl can provide is opening the correct printer device - whether that 
be the parallel port, the serial port, or a file. Once again the idea is that users can specify the printer 
device, and have their choice picked up by system software, without any conscious intervention to this 
effect by individual applications. 


All versions of the Series3 have form.dyl in their ROM, as do suitably reprogrammed versions of the 


Whilst the full power of the WDR printing system can be utilised only by object-oriented programmers, 
applications that are not themselves object-oriented may still be able to make considerable use of these 
services: 


= picking up the choices made by the user as regards the printer device (these choices are stored in 
environment variables) 


« reading the contents of .wdr files directly (by themselves) 
= reading the contents of .wdr files using services of the wdr class in form.dyl. 


In addition, the Hwif library contains routines hPrintSetupDialog, hPrinterSetupDialog, hPrint, 
hPrintSetSI, hPrintSensePageWidth, and hPrintSenseBufWidth, which layer over WDR printing services to 
achieve impressive results adequate for many purposes - without requiring any explicit use of object- 
oriented techniques (nor any explicit reference to the contents of .wdr files). See the Hwif Manual for 
more details. 


Creating .wdr files 


The creation of .wdr files is a quite separate process from their use, once created. Psion can supply a set 
of .wdr files covering some common printers, and other .wdr files may be available from third parties. 
However, it may prove desirable to produce a .wdr file for another printer not presently supported - or to 
enhance the .wdr file for a new version of a given printer. 


Note that whatever the origin of the .wdr file, the file name must not begin with fax. Such filenames are 
reserved for fax driver files and are automatically interpreted as such. 


One way to create .wdr files is by using the Sibo printer driver translator, wdtran.exe, which creates .wdr 
files from plain text input known as printer scripts typically having extension .wd. 


The operation of wdtran.exe is described later in this chapter. 


31 


ADDITIONAL SYSTEM INFORMATION 


The WDR printing environment variables 


WDR printing software may at various times attempt to read or write the four environment variables psp, 
PSS, PSF, and PSM: 


PSD is one byte long, having the value '0' to denote that the user has chosen to print using the 
parallel port, '1' to denote the choice of the serial port, and '2' to denote printing to file 


PSS is twelve bytes long, being a copy of the P_SRCHAR struct matching the choices last made by 
the user in any serial port and handshaking dialog(s) 


PSF can be up to P_FNAMESIZE (128) bytes long, being a copy of the filename last chosen by the 
user as the recipient of any data printed to file 


PSM can be up to P_FNAMESIZE+1 (129) bytes long, the first byte recording the printer model 
number (see below), and the remainder giving the full path name of the last . wdr file chosen 
by the user. 


Any software that attempts to read these environment variables should bear in mind that these variables 
do not always exist. Ordinarily, they are created only when the user makes an explicit choice via a 
dialog box. In the absence of one of the environment variables, the following defaults are assumed to 


apply: 
PSD effectively has the value '0', meaning that printing should be via the parallel port 
PSS see below 
PSF any printing to file is to the file p.lis 
PSM the printer model number is 0 and the printer driver file rom::bj.wdr should be used. 
The default values of the p_SRCHAR struct are as follows: 


P_SRCHAR ser; 


ser. tbaud=P_BAUD_9600; 
ser.rbaud=P_BAUD_9600; 

ser. frame=P_DATA_8; 

ser.parity=0; 

ser ..hand=P_OBEY_XOFF|P_OBEY_DSR|P_IGN_CTS; 
ser .xof f=0x13; 

ser .xon=0x11; 

ser.flags=0; 

ser.tmask=0; 


For more details on the P_SRCHAR struct, see the Serial Port chapter in the I/O Devices Reference manual. 
The essential point is that the contents of the P_SRCHAR struct should be used to P_FseT the serial port after 
opening it. 


For convenience, the printer model number is stored in printable form, with the value of '0' being added 
to its numerical value. Thus to use the second model in a .war file - so that the printer model number 
would be 1 - the first byte in psm should be 1+'0', ie '1'. 

A note on reading environment variables 

See the discussion on p_getenviron and p_getenv in the Plib Reference manual. 


Note that it is also possible to read and modify environment variables (eg for experimental purposes) by 
using the SIBO Debugger. 


SS ee Se ee ee 6 a a | 
Overview of the contents of a .wdr file 


This section provides an overview of the contents of a .wdr file. More details are provided in later 
sections. 


32 


3 WDR PRINTING 


Just about the simplest possible .wdr file would have the following contents, when dumped: 


0: 89 00 Oc 00 57 44 52 30 35 00 00 00 01 00 05 00 eee -WORO 5....... 
10: 47 65 6e 65 72 61 6c 00 00 00 00 00 00 00 00 00 General. .....2.. 
20: 00 00 00 00 00 00 00 00 1700 00 00 00 00 0000 .......... wee eee 
30: 00 00 00 00 00 00 00 01 Oc 01 Od 02 2a 0a 00 02~—i......... wee a ete 
40: 2a 20 00 00 00 00 00 00 01 00 02 05 23 4d 6f Ge ME cicvelorere}..olsis #Mon 
50: 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Ove ea ccc a snes 
60: 00 00 00 00 00 03 00 01 O00 f0 00 00 00 0000 00_~—i........ 
70: 00 00 01 00 01 00 01 00 01 16 00 90 00 f0 00 00... wee eee 
80: 00 00 00 00 00 01 00 04 00 04 00 28 00 48 00 4d_—ig..... sc (HOM 
90: 00 7b 00 89 00 oleae 


This conforms to the pattern 
<header><resources><index table> 
of Sibo standard .rsc resource files, as discussed in the Resource Files chapter in this manual. 


Indeed, .wdr files are but examples of .rsc files, with the extension changed to denote the particular 
purpose of being a wdr printer driver file. 


In fact, .wdr files come in two types - compressed and uncompressed (standard), corresponding to the 
.rzc and .7sc forms of resource file. Uncompressed files can be read by a variety of methods, as 
discussed in the Resource Files chapter. However, in order to read compressed .wadr files, it is more or 
less necessary to use some of the functionality of object-oriented classes in the ROM, using either: 


= the rscfile class in olib.dyl 
= the wdr class in form.dyl 


with the preference being for the latter, since it contains more explicit knowledge of the particular 
contents of .wdr files. 


Various services provided by the wdr class are described later in this chapter. 


Deciphering general.wdr 


The above dump is in fact that produced from an (uncompressed) version of the file general.wdr that is 
part of the Series3 ROM. This file describes the most basic kind of printer possible, possessing only one 
font, which is monospaced, and assumed to be 12 point (ie six lines per inch - one point corresponding to 
1/72 of an inch) and 10 cpi (characters per inch). For this printer, the only way to position the print 
head horizontally is by emitting space characters or carriage returns, and the only way to position the 
print head vertically is by emitting line feeds or form feeds. 


The index table for the file starts at file offset 0x89 (as contained in the first word in the file). Reading 
successive words from this index indicates that the individual resources in the file are to be found at file 
offsets 0x04, 0x28, 0x48, Ox4d, and Ox7b. 
The header resource 
The first resource in any .wdr file is always the header resource, having the following structure: 

= the first six bytes give a signature, which must always be "wor05" for a valid .wdr file 

= the next word gives the so-called wdr-flags for the file - evidently 0 in this case 


® the word after that gives the number of different printer models in the file - in this case, there is 
only one in the file 


s finally there is an array of WOR_MODEL_INDEX structs, one struct for each printer model in the file 


= each WOR_MODEL_INDEX starts with a word giving the resource identifier for the model resource 
(see later) giving more information about the printer model - this identifier has value 5 in this 
case 


= the WOR_MODEL_INDEX struct concludes with up to 24 bytes giving the public name of the model, in 
a zero-terminated string - "General" in this case. 


For general.wdr, the total size of the first resource is evidently 6+2+2+1*(2+24), ie 0x24. For other .wdr 
files which contain more than one printer model, the first resource will be larger. 


33 


ADDITIONAL SYSTEM INFORMATION 


The commands resource 
The second resource in any .wdr is always the commands resource. This consists of: 
= an array of command strings 
= preceded by a count byte (the first byte in the resource) 
= followed by a trailing word whose meaning is reserved for future expansion. 
In general.wdr above, the value of the count byte is 0x17, ie 23. 


Each command string that follows is given with a leading byte count, and without any terminating zero 
(as is appropriate, given that the command strings can in general contain embedded zeros). 


As can be seen, for general.wdr, all the 23 command strings are zero, except for (starting counting at 
zero): 


string 14 which has the value 0x0c 
string 15 which has the value 0x0d 
string 16 which has the value '*' 0x0a 
string 18 which has the value '*' 0x20 


The first 21 of the command strings in the command block have reserved meanings, best described in 
simple terms by giving the descriptive text associated with these commands in .wd files (see Contents of 
.wd files section later): 


"RESET" 
"FORM_LENGTH" 
"PREAMBLE" 
"POSTAMBLE" 
"UNDERLINE_ON" 
"UNDERLINE OFF" 
"BOLD_ON" 

"BOLD OFF" 
"ITALIC_ON" 
"ITALIC_OFF" 

10 “SUPERSCRIPT_ON" 
11 "SUPERSCRIPT_OFF" 
12 "SUBSCRIPT_ON" 
13 "SUBSCRIPT_OFF" 
14 “NEW PAGE" 

15 “CARRIAGE_RETURN" 
16  "MOVE_DOWN" 

17 "MOVE_RIGHT_PREFIX" 
18 "MOVE_RIGHT" 

19 "MOVE_RIGHT_SUFFIX" 
20 “LANDSCAPE 


OMNAUEP WN Oo 


Additional command strings can be used to set various fonts, and are referenced by typeface resources, 
discussed below. 


Model resources 


The resource identifiers of all model resources are listed in the header resource. As mentioned above, the 
only model resource in general.wdr has identifier 5. Consulting the index table for the file indicates that 
this resource starts at file offset 0x7b (recall that resource identifiers start at 1). 


The structure of a model resource is as follows: 


s The first five words give the values of the so-called minx, miny, skipx, skipy, and model-flags 
for the printer model. 


= Next comes a word giving the number of following typeface resource identifiers. 
® This is followed by an array of the specified number of identifiers of typeface resources. 
For general.wdr, the values of the various values are evidently as follows: 


minx 0x90 (144) 


34 


3 WDR PRINTING 


miny Oxf0 (240) 
Skipx and skipy both zero 
model-flags ZeTO 


and there is just one reference to a typeface resource which, in this case, has resource identifier 4 (which, 
via the index, locates it at file offset 0x4d). 


Typeface resources 
The structure of a typeface resource is as follows: 


® The first 20 bytes contain the public name of the typeface, as a zero-terminated string. The text 
of this name must not exceed 16 characters, not including the terminating zero. 


= The next word gives the typeface number of the typeface. 
= The word following contains typeface-flags. 


= Next comes a word which, if non-zero, contains the identifier of a translates resource to be used 
by the typeface. 


= The word following that gives a count of the number of different sizes (or fonts) that the 
typeface comes in. 


® Finally there is an array of WOR_FONT structs, one struct for each font size supported by the 
typeface. Each wor_FONT struct consists of nine words: height, height_max, height_delta, 
width_scale, width_normal, width_italic, width_bold, width_italic, width_bold_italic, and 
concludes with the number of the command string, in the commands resource, of the associated 
printer instruction to set this particular font. 


The difference between a “typeface” and a "font" is discussed in more detail below. 


In general.wdr, the public name of the only typeface in the file is "Mono", the typeface number and the 
typeface flags are both zero, the translates resource with identifier 3 is to be used, and there is only 1 
WDR_FONT struct following in-line. 


In a WOR_FONT struct: 
® The fields height_max and height_delta are only relevant for so-called scalable typefaces 
« The field width_scale is only relevant for proportional typefaces 


= For monospaced typefaces, the values of the four fields width_normal through width_bold_italic 
are to be interpreted as real numbers; for proportional typefaces, in which the widths of the 
characters vary from character to character, these values are identifiers of font width table 
resources. 


There are no font width table resources in general.wdr. Incidentally, font width tables are stored in 
difference form in .wdr files - see later for more details. 
Translates resources 


The first word in a translate resource gives the number of translates that follow in-line. 


Each translate starts off with a byte giving the length of the remainder of the translate. The next byte is 
the Ascii value of the character to be translated, and that is followed by a sequence of bytes into which 
the character is to be translated. 


In general.wadr, there is only one translates resource, which in turn only contains one translate, whose 
effect is to convert every character with Ascii value 0x05 into one with value 0x23. This results in 
telephone symbols (recorded internally on the Series3 as 0x05's) being printed as hash signs. 
Summary of resource types in a .wdr file 


The above survey contains one example of every possible type of resource in a .wdr file, except for font 
width table resources. 


In summary, the possible resource types are: 


header (always the first resource in the .wdr file) containing an index of all the printer 
models supported by the file, as well as some important wdr-flags 


35 


ADDITIONAL SYSTEM INFORMATION 


commands (always the second resource in the .wdr file) containing the character command 
sequences for resetting the printer, controlling the text format, moving the 
print head position, selecting specified fonts, and so on 


translates used to map the printer's character set onto that used by the SIBO computer 
(which is based on IBM code page 850) 


font width tables defining the widths of all the characters in proportional typefaces 


models giving essential data governing the capabilities of a printer, and listing the 
typefaces supported by the printer 


typefaces describing the typefaces supported by a printer model, including the various 
“font sizes" available for that typeface. 


A ee re || 
More details on the contents of .wdr files 


Possible wdr-flags 


The only wdr-flag of any general significance is woR_DYL_LOAD, with value 0x01. If set (in the header 
resource), this means that the contents of the .wdr file are insufficient, by themselves, to describe the 
behaviour of the printer fully, and that the wdr printing system software should load a suitable external 
dyl to additionally customise the print behaviour. 


Examples of .wdr files with the wor_DYL_LoaD flag set are the printer driver files for postscript printers. 
Further discussion of loading additional printer dyls is beyond the scope of this document. 
Other bits set in the wdr-flags may have special significance for code in the extra print dyls. 


The notion of “printer models” 
The notion of a printer model is essentially a device to cover more than one printer using the same data. 


Different printer models can be described in the same .wdr file, even if they support different sets of 
typefaces or have other differing characteristics, so long as they have the same basic set of printer 
command strings (and the same wdr-flags). 


The public name of a printer model is what is presented to the user in any dialog offering a list of 
“printer models" for selection. 


Overview of the different command strings 


As well as the command strings to select various fonts, a .wdr file contains commands to have the printer 
perform other functions. These commands are by and large clearly named in the listing given earlier, eg 
"ITALIC_ON" and "ITALIC_OFF", "BOLD_ON" and "BOLD_OFF", and "SUPERSCRIPT_ON" and "SUPERSCRIPT_OFF". 


If a printer cannot support a given feature, the corresponding command string would generally be left 
null (*"), One possible exception is italic which, if not supported, could be implemented as an underline. 
(Note incidentally that it is possible for a printer which supports italic in one font not to support it in 
another font, and so on. Again, a printer may support both italic and superscript, but not both at the 
same time.) 


The "LANDSCAPE" command, if non-null, is the command to cause the printer to enter landscape mode (as 
opposed to portrait mode). 


The string of commands sent to the printer when printing starts are among the most important, as regards 
influencing the printed outcome. The very first command the software sends the printer is the "RESET" 
command. Then it sends a "FORM_LENGTH" command, and then the "PREAMBLE" command, before starting to 
print the document proper (together with headers and footers, etc). At the very end, a "POSTAMBLE" 
command is sent. 


The “POSTAMBLE" command may be needed to flush the printer buffer, and to restore the printer to its 
default settings. 


The "PREAMBLE" Command may have such drastic consequences as choosing the basic configuration of the 
printer (possibly overriding defaults set via dip switches). In any case of doubt, the documentation for a 
particular printer should be consulted carefully. 


36 


3 WDR PRINTING 


Special characters in command strings 


The commands "MOVE_RIGHT", "MOVE_DOWN", and "FORM_LENGTH" are each used in conjunction with a value 
passed by the printer subsystem software. For example, the commands are to set the form length zo a 
given value, or to move the printer position right by a given amount. This value may either end up in the 
command string by a process of substitution, or it may result in the command being repeated as required: 


= if any of these commands is defined as starting off with an asterisk character ('*'), what is 
actually sent to the printer is the remainder of the command string (ie minus the initial asterisk) 
repeated the specified number of times 


« if the string "%d" is contained within the command string, the specified value is converted into 
decimal representation and is substituted for the "%d" (like printf in C) 


= likewise the string "%c"' means to substitute the specified value as a single byte (character), and 
"%w" means to substitute it as a pair of bytes (low byte first). 


For example, in some printer drivers "MOVE_DOWN" is defined as "*<10>", so that "MOVE_DOWN n" will be sent 
to the printer as n line feed characters (line feed is Ascii 10). 


Again, in the HP Laserjet III printer driver, "move_pown" is defined as "<27>&at+%dv", so that "MOVE_DOWN 6" 
(say) will be sent to the printer as "<27>&a+6v". 


This kind of substitution can also take place in the command strings to select a specific size of a so-called 

scalable font - see below. 

The MOVE_RIGHT commands 

For some printers, the command to move right by a certain amount is of the general form 
<prefix><repeated body><suffix> 


with the central part being repeated as many times as required, depending on the amount by which the 
print position is to be adjusted. 


It is to cope with this case (as well as ones even more complicated) that the commands 
"MOVE_RIGHT_PREFIX" and "MOVE_RIGHT_SUFFIX" are provided. These will be left null for most printers. 


Printer units 


The units for the "FoRM_LENGTH" command are always 1/6 of an inch. Thus if the print software wishes, 
as part of initialising a printer, to set the form length to 12 inches, the command 


"FORM_LENGTH 72" 
should always be sent. 


However, the units used in many other features of printer driver files varies from printer to printer. The 
key quantities are the values of minx and miny, as specified in the model resource for a printer. 


Minx and miny are themselves standardly given in so-called twips, where twenty twips make a point (so 
that 1440 twips make an inch). For example, in the file general. wdr discussed above, minx has the value 
144 twips, ie 1/10 of an inch, and miny has the value 240 twips, ie 1/6 of an inch. This matches the 
basic assumptions made in general.wadr that the font printed is 12 point and 10 cpi (see earlier). 


The fundamental significance of minx is that this is the smallest amount by which the print position can 
be adjusted horizontally. Similarly, miny is the smallest amount by which the print position can be 
adjusted vertically. Clearly, the smaller minx and miny are, the higher the resolution of the printer. 


The above values make sense for general.wdr since the only way the print position can be adjusted 
horizontally is by emitting a space character (or by emitting a carriage return, which resets the horizontal 
position), and the only way the print position can be adjusted vertically is by emitting a linefeed character 
(or by emitting a formfeed character, which effectively resets the vertical position). 


A command such as "MOVE_RIGHT n" actually means to move the print position right by n times minx, and 
similarly a command such as "MOVE_DOWN m" means to move the print position down by m times miny. 


The value of skipx for a printer model is subtracted from the first "MovE_RIGHT" command in each line of 
text, to compensate for the fact that many printers cannot print at the left edge of the paper. The value of 
skipy is likewise subtracted from the first "MovE_bpoWN" command in each page, to compensate for the fact 
that many printers cannot print at the very top of the paper. 


37 


ADDITIONAL SYSTEM INFORMATION 


The values of skipx and skipy are themselves expressed in terms of minx and miny, respectively. Thus if 
skipy is given as 36 whereas miny is given as 20, this translates to an actual height of some 20*36 twips, ie 
half an inch, at the top of the paper which is inaccessible to the printer. 


Possible model-flags 
There are two bits that can be set in the model-flags in a model resource: 


™ WDR_MODEL_LANDSCAPE_AVAILABLE (0x01) has to be set if the model supports being put into 
landscape mode 


®@ WDR_MODEL_MINX_IS_DOTS_PER_INCH (0x04) should be set if the value of minx is expressed, not in 
twips (as standard), but in reciprocal inches (so that a minx of 300 would correspond to 1/300 of 
an inch - which is not expressible as an exact number of twips). 


Note that even if WR_MODEL_MINX_IS_DOTS_PER_INCH is set, the value of miny is always expressed in twips. 


Typefaces and fonts 


A typeface is considered to be a set of characters in a particular style, whereas a font is a particular size 
of a typeface. 


The public name of a typeface is what is presented to the user in any dialog offering a list of "fonts" 
(actually typefaces) for selection. The various different fonts within a chosen typeface will be selected 
via a secondary choice list, keyed by the notional height of the fonts. 


There can be considerable scope for authors of .wdr files in deciding how to represent the different fonts 
supported by a printer (especially dot matrix printers). For example, many dot matrix printers support a 
condensed font: this may be represented as a typeface called "Pica condensed" (say) or as a smaller font 
of the "Pica" typeface. It is generally more useful for the user to have a number of size variants (fonts) 
of one typeface rather than a number of typefaces each having only one size. For this reason the second 
of the above approaches is the recommended one. It also has the advantage that the typeface names will 
then (generally) be language independent, whereas the addition of a phrase such as “double width" or 
"condensed" immediately makes the .wdr file language dependent. 


A dot matrix printer may support a number of variants of a font including: condensed, double height, 
double width, condensed double width etc. For a twelve point base font it is recommended to map these 
to the following heights: 


condensed 7 point (140 twips) 

normal 12 point (240 twips) 
condensed double width 13 point (260 twips) 
double width 16 point (320 twips) 
double height 22 point (440 twips) 
double height double width 24 point (480 twips) 


Note that a taller font must have a larger size than a shorter font, in particular the point sizes of all the 
double height fonts must be larger than all the single height fonts. 


If a dot matrix printer supports a large number of variations on a base font, it may turn out that two 
different variations would be mapped onto the same point size. In this case it would be perfectly 
acceptable to just omit one of the fonts: if there are already 12,13,14,15 and 16 point fonts available then 
omitting (say) a second 14 point is not really a hardship to the user. Alternatively, the font could be 
incorporated in another typeface. In case it is decided to omit a font, bear in mind that double height 
fonts generally look much better than double width fonts, so given a choice it is better to omit the latter. 


Typeface numbers 


The main significance of the typeface number of a typeface is when a document set up for one printer is 
subsequently printed on another printer. Font "substitutions" have to be made - and these are done 
according to the values set for the typeface number. 


Thus if a document is prepared for one printer model, and some text is to be printed in a typeface having 
typeface number 2, say, and then the user changes the printer model setting for the document to another 
printer, that text, when printed, will be printed in the first typeface found in the new printer model, 
having the same typeface number. 


38 


3 WDR PRINTING 


In case no exact match in typeface number is possible, the first typeface defined in the new printer model 
is used instead. 


There are a large number of allowed typeface numbers, listed later in this chapter. Note that typeface 
numbers are completely independent of the public names of typefaces. 


Typeface numbers may also be used in some forms of RTF file conversion. 


Possible typeface-flags 
There are three bits that can be set in the typeface-flags in a typeface resource: 


S WOR_TYPF_PROPROTIONAL (0x01) set if the widths of the characters in a font in this typeface can vary 
among themselves (the alternative is that the fonts are monospaced) 


" WDR_TYPF_SCALED (0x02) set if the fonts supported by the typeface are all generated by the printer 
as being different scaled versions of one common pattern 


= =WDR_TYPF_SERIF (0x04) set if the typeface is serif. 
The wOR_TYPE_SERIF flag has significance only in certain types of RTF file transfer. 


Note that scalable monospaced typefaces are not supported, so that scalable fonts always have to be 
regarded as proportional, even if the widths of their characters do not vary in fact. 


For scalable typefaces, there is only one woR_FONT struct per typeface, with all required information for 
differently sized fonts being generated from this by arithmetical scaling. 
Heights of fonts 


The command string of a scalable typeface must include some kind of parameter (eg "%d") for the 
particular size required to be specified when setting the font. This parameter will be filled in by system 
software giving the height of the required font in point units. 


However, heights of fonts in .wdr files are always given in twips (thus 240 for a 12 point font). 


For non-scalable fonts, the height_max and height_delta fields in a woR_FONT struct are meaningless. For 
scalable fonts, the set of supported font sizes is obtained by repeatedly incrementing by height-delta, 
from the value of height to the value of height_max (so that the "height" field actually plays the role of a 
“height_min" field). 


In general, 4 points (80 twips) is a sensible minimum height for a font, whereas the maximum allowed 
height is determined by the fact that the widths of characters must at all times remain less than 255. 
Widths of characters in fonts 


Widths of characters in fonts in .wdr files are expressed - as are all horizontal measurements in .wdr files 
- in units of minx. 


The wdr system allows for widths of characters altering as they are italicised or bolded, and again when 
they are simultaneously italicised and bolded. In case there is no such variation, the values of the fields 
width_normal, width_bold, width_ italic, and width_bold_italic, will merely duplicate each other. 


For proportional fonts, these four fields each refer to font width tables. These are stored in difference 
form in .wdr files. Thus if the array of stored widths is stored(}: 


® width of character 0 = stored(0] 

® width of character 1 = width of character 0 + stored{1] 
« width of character 2 = width of character 1 + stored{21 
« width of character 3 = width of character 2 + stored(3] 


and so on. The rationale for this is that the differences are frequently zero, so that the font width table is 
stored as an array, many of whose values are zero. In tur, this compresses much more markedly (for 
compressed versions of .wdr files) than tables containing many different values - with the result that 
smaller .wdr files get produced (bear in mind that font width tables potentially make up large parts of 
these files). 


The values obtained from a font width table should all be multiplied by the width_scale value for the 
font. This mechanism avoids needless duplication of data in which two font width tables would 
otherwise both be present in a .wdr file, even though one is merely a scaled version of the other. 


39 


ADDITIONAL SYSTEM INFORMATION 


For scalable fonts themselves, the values in the font width table (once multiplied by any width_scale 
value for the font) are what would apply to a fifty point high version of the font. These have to be 
further scaled, in general, to match the chosen height of the font. 


Note that in all cases, widths of characters cannot exceed 255. 


ia ee ee ee ee ee ee 
Creating .wdr files using wdtran.exe 


The printer driver translator is a tool wdtran.exe that operates on a so-called printer script, which is a 
text file, to produce a printer driver file as output. 


The process is akin to ordinary compilation: 
*,.c + compiler -> *.obj 
*,wd + printer driver translator -> *.wdr 
with .wd being the usual extension for a printer script. 
Some sample .wd files are distributed as part of the optional component of the Sibosdk. 
To produce eg general.wdr from general.wd, simply type 


wdtran general 


Contents of .wd files 


The basic contents of .wd files correspond to the different resources in .wdr files (see earlier in this 
chapter). 


A .wd file consists of a number of resource definitions, of which there are five types: COMMANDS, 
TRANSLATES, WIDTHS, TYPEFACE, and MODEL. 


There must be one and only one commaNDs resource in a .wd file. There must be at least one MODEL 
resource and at least one TYPEFACE resource, though there can be more of each. There can be any number 
(including zero) of WIDTHS and TRANSLATES resources. 


Note that there are no definitions in a .wd file directly corresponding to the header resource in a .wdr 
file. The header resource is specially created by wdtran.exe. 


Each resource definition consists of a header, a number of commands, and then a footer. The header is 
of the form 


<RESOURCE> [identifier] 


with the identifier being required only if the resource is to be referenced by another resource (it may be 
the name of a width table, for example). 


The resource footer is of the form: 
END_<RESOURCE> 
Each intervening command is of the form: 
keyword [parameter] 
For example, the definition of a TYPEFACE resource looks like 


TYPEFACE pica 


END_TYPEFACE 


3 WDR PRINTING 


and the definition of a comMANDS resource looks like (this is the commanps resource for the file general.wd): 


COMMANDS 
RESET ut 
FORM_LENGTH me 
PREAMBLE Hes 
POSTAMBLE wet 
BOLD_ON eat 
BOLD_OFF anet 
ITALIC_ON aM 
ITALIC_OFF a 
UNDERLINE_ON me 
UNDERLINE _OFF a 
SUBSCRIPT_ON baat 
SUBSCRIPT_OFF He 
SUPERSCRIPT_ON es 
SUPERSCRIPT_OFF st 


MOVE_DOWN Wee 10>" 

MOVE_RIGHT_PREFIX =" 

MOVE_RIGHT We 32>" 

MOVE_RIGHT_SUFFIX =o 

NEW_PAGE m<12>u 

CARRIAGE_RETURN N<13>" 
END_COMMANDS 


Parameters to keywords inside a resource definition may be of four types: 


Numeric a decimal number which is interpreted as a value (eg a font width) 

Constant an unquoted string which is interpreted as a numeric value (used only by TYPE 
in a TYPEFACE resource definition) 

Identifier a lower case unquoted string, used by a command to reference another 
specified resource 

Quoted string a sequence of characters in quotes that is sent to the printer to perform a 
specific function (eg BOLD_oN); the string may contain control characters (see 
below). 


The printer driver translator ignores any characters between an exclamation mark ! and the end of a line 
(unless the ! is in a quoted string). Blank lines are also ignored. 


The block indentation scheme in standard .wd files is for convenience only. 


Acceptable syntax within a COMMANDS resource definition 


Any of the commands listed in the earlier section on command resources in .wdr files can be given. 
However, command strings used to set individual fonts should be given within the relevant TYPEFACE 
resource definition. 


Control codes may be needed in the COMMANDS resource definition, or in the COMMAND keyword of a font 
definition. A control code is specified by placing its decimal value in angle brackets ("<" and ">"). For 
example the Esc control code (decimal value 27) is specified as <27>. 


Other syntax that may occur within a COMMANDS resource definition are: 


USE_DYL to set the woR_DYL_LoaD flag in the wdr-flags in the header resource for the .wdr 
file 

FLAGS <flags> to or in <flags> to the wdr-flags in the header resource 

HP_PCL_COMPATIBLE of purely historical significance. 


Acceptable commands within a TRANSLATES resource definition 


Each line within the definition of a TRANSLATES block should be made up of one or more translations, with 
adjacent translations on any one line being separated from each other by white space. 


Individual translations should have the form 


<byte>:<byte> 


41 


ADDITIONAL SYSTEM INFORMATION 


or else 
<byte>:<string> 
For example: 
5:35 
156:"<27>R<3><35><27>R<0>" 
Note that any width table for a font which uses translates must contain the correct width for each 
character after it is translated. The printer driver translator does not check this. 
Acceptable commands within a WIDTHS resource definition 


Each line within the definition of a wiptus block should be made up of one or more character widths, 
with adjacent character width definitions on any one line being separated from each other by white space. 


Individual character width definitions should have the form 
<byte>:<width> 


Note that in contrast to .wdr files, which store font width tables in differenced form (see earlier in this 
chapter), .wd files should define the widths of all characters in absolute terms. The differencing is 
performed by wdtran.exe (just as the converse integration is performed by the wdr class in form.dyl 
without the conscious intervention of any application software). 


The printer driver translator will give an error if the widths of the non-breaking hyphen, potential 
hyphen, and standard hyphen (character codes 7, 14, and 45) are not all the same. 


Likewise, the widths given for the non-breaking space, the standard space, and the tab (character codes 
15, 32, and 9) also all have to agree. 


Acceptable commands within a TYPEFACE resource definition 
The following commands may be present within the definition of a TYPEFACE resource: 


PROPORTIONAL to set WOR_TYPF_PROPORTIONAL in the typeface-flags 
SCALED to set WOR_TYPF_SCALED in the typeface-flags 

SERIF to set WOR_TYPF_SERIF in the typeface-flags 
MULTIPLE_FONT_WIDTH_TABLES of purely historical interest 

NAME <name> to give the public name of the typeface 

TYPE <type> to give the typeface number of the typeface 
TRANSLATE <identifier> to specify a TRANSLATES resource to be used 

FONT to commence the definition of a FonT sub-resource. 


The Font keyword must appear at least once in each TYPEFACE definition, and can appear more than once. 
The other keywords should only appear once, at the most. 


Each FONT sub-resource definition follows the same general pattern as the other resources: 


FONT 
<keywords> 


END_FONT 


Possible keywords in the body of a FonT resource definition are HEIGHT, HEIGHT_MAX, HEIGHT _DELTA, 

WIDTH_SCALE, WIDTH, WIDTH_BOLD, WIDTH_ITALIC, WIDTH_BOLD_ITALIC, and COMMAND - each having the 

straightforward meaning of specifying a corresponding field within the wor_FoNT structure for that font 

(see earlier in this chapter). 

Acceptable commands within a MODEL resource definition 

The following commands may be present within the definition of a MODEL resource: 
LANDSCAPE_AVAILABLE to set WOR_MODEL_LANDSCAPE_AVAILABLE in the model-flags 


MIN_X_IS_DOTS PER_INCH to set WOR_MODEL_MINX_IS_DOTS_PER_INCH in the model-flags 


42 


3 WDR PRINTING 


NAME <name> to give a public name for the printer model 
TYPEFACE <identifier> to specify a TYPEFACE resource supported by the printer model. 


Additionally, the keywords MIN_X, MIN_Y, SKIP_X, and SKIP_Y straightforwardly specify the minx, miny, 
skipx, and skipy values for the printer model. 


In contrast to the case for TYPEFACE resources, which can only have one public name each, MODEL resources 
can have more than one public name each. This avoids needless duplication in a .wd file, if it tums out 
that two MODEL blocks would otherwise be identical. Note that one MopEL resource having two public 
names is not in general the same thing as there being two different moDEL resources in the same .wd file 
(though in each case, there will be two distinct entries in the Printer Model choice list). 


Allowed typeface numbers 
TYPE may take any of the values: 


0 COURIER 22 OPTIONAL_SB 44 RUSSIAN 

1 PICA 23 OPTIONAL_SC 45 OPTIONAL_B 

2 ELITE 24 TIMES_ROMAN 46 OPTIONAL_C 

3 PRESTIGE 25 CENTURY 47 OPTIONAL_D 

4 LETTER_GOTHIC 26 PALATINO 48 NARRATOR 

5 GOTHIC 27 SOUVENIR 49 EMPHASIS 

6 CUBIC 28 GARAMOND 50 ZAPF_CHANCERY 
7 LINEPRINTER 29 CALEDONIA 51 OPTIONAL_DA 
8 HELVETICA 30 BODONI 52 OLD_ENGLISH 
9 AVANT_GARDE 31 UNIVERSITY 53 OPTIONAL_DB 
10 SPARTAN 32 SCRIPT 54 OPTIONAL_DC 
11 METRO 33 SCRIPT_PS 55 COOPER_BLACK 
12 PRESENTATION 34 OPTIONAL_SCA 56 SYMBOL 

13 APL 35 OPTIONAL_SCB 57 LINE_DRAW 

14 OCR_A 36 COMMERCIAL_SCRIPT 58 MATH_7 

15 OCR_B 37 PARK_AVENUE 59 MATH_8 
16 STANDARD_ROMAN 38 CORONET 60 DINGBATS 

17 EMPEROR 39 OPTIONAL_SCC 61 EAN 

18 MADELEINE 40 GREEK 62 PC_LINE 

19 ZAPF_HUMANIST 41 KANA 63 OPTIONAL_SYA 
20 CLASSIC 42 HEBREW 
21 OPTIONAL_SA 43 OPTIONAL_A 


Suppose, for example, a user has a document written when the Apple Laserwriter printer driver was 
selected, and the document uses two typefaces, Times Roman and Palatino. The user then changes to a 
HP Laserjet III printer driver. Times Roman on the Laserwriter and CGTimes on the HP IT both have a 
TYPE Of TIMES_ROMAN and so the Times Roman typeface would be mapped to CGTimes. The HP III driver 
has no typeface with a Type of PALATINO and so the Palatino font would be mapped to Courier. 


The following rules are useful for guidance: 


One of the printer's monospaced typefaces should be assigned a Type of courteRr: this should be the 
typeface described as Courier or perhaps Pica in the printer's manual. The typeface's name should be as 
in the manual (eg Pica). 


If a printer supports only one proportional typeface it should be assigned a Type of TIMES_ROMAN, although 
it may be given a different NAME (eg Proportional). 


If a printer supports more than one proportional font then the one with serifs (if available) should be 
assigned a TYPE Of TIMES ROMAN and the one without serifs should be assigned a Type of HELVETICA; their 
NAMES should, however, be those used in the printer's manual, eg CGTimes and Univers for the HP 
Laserjet III. 


See also the Printer driver font mapping section of the Word Processor File Format chapter. 


43 


CHAPTER 4 


DBF FILES 


ey a a oe ae ee ee a ee ee] 
Introduction 


Database files (DBF files) are binary files containing typed, variable length records. Many SIBO 
applications (for example, the MC Diary and the Series 3 Database) store their data in DBF files. The 
data files created and manipulated by OPL are also examples of DBF files. 


Database files are designed to be Flash-friendly, that is, they may be stored and manipulated in Flash 
SSDs (or any other EPROM medium). A DBF file stored on such a medium may be modified by 
appending, deleting, or replacing records without having to make a new copy of the entire file. 


The chapter Database Files in the PLIB Reference manual describes DBF files from the point of view of 
reading and writing them using PLIB function calls such as Dbfopen, DbfAppend, DbfFindRead, and 
DbfNextRead. The JSAM Manual discusses more advanced techniques for using DBF files, in conjunction 
with independent keyed index files. The present chapter focuses instead upon a description allowing 
access to DBF files independently of these specialised PLIB and ISAM functions. 


First, the basic structure of a general DBF file is reviewed, and then particular examples are given of 
how various SIBO applications use DBF files. This information should assist the creation of file format 
conversion programs such as might run on another computer, for example converting between Series 3 
Agenda files and files that can be read directly by PC-based PIMs (Personal Information Managers). 


a ee ee a eae 
Basic structure of DBF files 


The following discussion is based around a DBF file created by a simple OPL program. It is not 
necessary to be familiar with OPL to follow the discussion, since the contents of the DBF file are 
described independently of the OPL program. It just happens that OPL is a convenient way to create 
DBF files quickly, so that experimentation is easier. 


The OPL program creating the file is as follows: 


PROC writedbf: 
if exist("test.dbf") :delete "test.dbf" sendif 
create "test.dbf",a,f1%, 2%, 3%, f4 
a. f1$="Hello"” 
a. feg=au 
a. f3%=3 
a. f4=4 
append 
a. f1$="World"' 
append 
close 
beep 5,300 
ENDP 


The create statement creates a DBF file with the name test.dbf, by default in the \opd\ top-level 
directory on the default drive. This file is assigned the logical handle a inside the program. Each record 
in the file is to have four fields (called £18, f2$, £3%, and #4 inside the program). Standard OPL naming 
conventions mean that these fields have types string, string, integer, and double, respectively. 


45 


ADDITIONAL SYSTEM INFORMATION 


The two append statements mean that two records are written to the file, before it is closed. 
Dumping the resulting file yields the following: 
O: 4f 50 4c 44 61 74 61 62 61 73 65 46 69 6c 65 00 OPLDatab aseFile. 


10; Of 11 16 00 Of 11 04 20 03 03 00 02 12 10 05 48 — wane wee eee H 
20: 65 6c 6c 6f 07 32 03 00 00 00 00 00 00 00 10 40 eOLlLO.2.. weeccee a 
30: 12 10 05 57 6f 72 6c 64 01 32 03 00 00 00 00 00 eeeWorkd .2...... 
40: 00 00 10 40 ee) 


This conforms to the standard DBF format of 


<standard header><extended header><field information record><other records> 


where 
<standard header> always has length 22 bytes. 
<extended header> can have variable length and is frequently of zero length (as 
here). 
<field information record> gives the structure of all type I records following. 
<other records> are the main body of the DBF file. 


The standard header 


The first sixteen bytes of all standard DBF files are the zero-terminated string "opLDatabasef ile". This 
string is used by all SIBO applications. Applications are allowed to use alternative strings although such 
DBF files can not be read by OPL. 


The two bytes at offset 0x10 and the two bytes at offset 0x14 represent software version numbers. See 
the System Information section of the General System Services chapter of the PLIB Reference manual for 
an explanation of the format of a version number. 


The two bytes at file offset 0x10 represent the version number of the software that generated the file. 
Files generated by the Series 3 and Series 3a Data applications have these bytes set to <0f><10>. Files 
generated by OPL set these two bytes differently on different machines: on the HC and Series 3 they are 
set to <Of><11> and on the Series 3a and Workabout they are set to <1f><11>. If generated by version 1 of 
ISAM the two bytes are set to <00><10> (which, strictly speaking, is not a legal version number). 


The two bytes at file offset 0x14 represent the minimum version number of DBF software that is required 
to interpret the contents of the file. Files generated by the Series 3 and Series 3a Data applications have 
these bytes set to <0f><10>. Files generated by OPL set these two bytes to <0f><11> on all machines. If 
generated by version 1 of ISAM the two bytes are set to <00><10> (which, strictly speaking, is not a legal 
version number). 


The two bytes at file offset 0x12 contain the file offset for the start of the field information record. Thus 
in the absence of an extended header they would contain <16><00> and for an extended header of length 
256 bytes they would contain <16><01>. 

The extended header 

A DBF file has an extended header only if an application calls the PLIB function pbf€xtHeaderwrite. At 
the time of writing SIBO applications neither make this call nor make use of the extended header. 

The field information record 

The field information record is a type 2 record. 


The first byte contains the number of fields defined for each rype J record (see below for an explanation 
of type I records). In the above example four fields were defined. The number of fields defined must be 
an integer between 1 and 32 inclusive 


The second byte always contains <20> (see below for an explanation). 


The remaining bytes contains the field types. The first byte contains the type of the first field, the second 
byte contains the type of the second field and so on. The following types are allowed: 


= a type of 0 means that the field is an integer: a numeric value stored in two bytes, low byte first. 


= a type of 1 means that the field is a Jong: a numeric value stored in 4 bytes. 


46 


4 DBF FILES 


= a type of 2 means that the field is a double: a floating point value stored in 8 bytes in standard 
IEEE format. 


= a type of 3 means that the field is a string: a sequence of up to 255 bytes preceded by a byte 
giving the length of the sequence. 
The format of all records 


All records contain a two byte header followed by a sequence of bytes constituting the body of the 
record. 


The highest nibble of the header word contains the record's type. The lowest three nibbles contain the 
length of the record body (a nibble is four bits, thus each byte consists of two nibbles). This explains 
why the second byte in the header of the field information record is always <20>. 


In the example DBF file (see above) the record immediately following the field information record has 
<12><10> as its header and is thus a type J record, with a body of length 0x12 bytes. 


Counting past another 0x12 bytes leads to the header of the following record, which is also <12><10>. 
Counting along yet another 0x12 bytes leads precisely to the end of the file. 


Since each of these records are type ] they must all conform to the internal structure specified in the field 
information record: 


= a leading byte-counted string, for the first string field. 
= asecond leading byte-counted string. 

=" two bytes for the integer field. 

® eight bytes for the double field. 


Looking more closely at the above dump, it can now be appreciated how the details of the file contents 
match the earlier OPL program. 


Generally speaking, the only types of record that will be found in any DBF file produced by a SIBO 
application are: 


type 0 deleted record. 

type 1 standard record. 

type 2 field information record. 
type 3 descriptive record. 


Deleted records 
Consider the following OPL program, which differs from the earlier one in only one line: 


PROC writedbf: 
if exist("test.dbf") :delete "test.dbf" sendif 
create "test.dbf",a,f1$, f28, 3%, £4 
a.fi$="Hello" 
a.f2g="2" 
a.f3%=3 
a.f4=4 
append 
a. f1$="Wortd" 
update 
close 
beep 5,300 
ENDP 


The change is that the second append instruction has become an update instruction, so that the end result 
is that the file has only one record. 


Dumping the DBF file output by this second program gives (provided the file is created on a Flash SSD): 
O: 4f 50 4c 44 61 74 61 62 61 73 65 46 69 6c 65 00 OPLDatab aseFile. 


10: Of 11 16 00 Of 11 04 20 03 03 00 02 12 0005 48 ~~... wee H 
20: 65 6c 6c 6f 01 32 03 00 O00 00 00 00 00 00 10 40 COE. oasis s.0'e0 a 
30: 12 10 05 57 6f 72 6c 64 01 32 03 00 00 00 00 00 ---World .2...... 
40: 00 00 10 40 oo of 


47 


ADDITIONAL SYSTEM INFORMATION 


This differs from the earlier dump only with regard to one nibble which gives the record type for the first 
record in the file. The two bytes <12><10> at file offset 0xic have changed into <12><00>, indicating that 
while the same length of data remains on the file, the first record is no longer type 1, but type 0, i.e. it 
has been deleted. 


Erased records remain in a DBF file until such time as the file is written out again, say in response to a 
"Save As" menu command, or until the file is compressed (say in response to a "Compress" menu 
command). (In fact, OPL programs automatically attempt to compress their DBF files whenever they are 
closed, which explains why the above example gives different results unless the file is created on Flash.) 


Although erased records may remain part of a DBF file long after the application has “deleted” or 
"updated" them, they are inaccessible to normal software (eg to the PLIB bbfxxx calls and the OPL 
database functions such as find and count). 


For more discussion about the mechanism of deleting records, see the Database Files chapter in the PLIB 
Reference manual. 


The remaining examples of DBF files in this chapter all assume that any deleted records have been 
removed. 
Descriptive records 


From most points of view, records of type 3 and upwards are all potentially "singular" records without 
the usual software support. Operations such as "Find" do not usually find text within such records as 
these record types are not used by standard SIBO applications (with one important exception, discussed 
below). See the Database Files chapter in the PLIB Reference manual for more details of these record 


types. 


The exception is with a record of type 3, which is a so-called "descriptive" record. See later in this 
chapter for examples of data that may be stored in a descriptive record. 


There is usually at most one record of type 3 in any one DBF file. 


By convention, a descriptive record's data is composed of a number of typed fields. Each field has a two 
byte header that contains the field's type and length, in the same format as for a record header. 


The types have variable meaning, depending on the application. Given that different versions of an 
application may define different types of field within the descriptive record, it is good practice for 
applications that encounter fields in a descriptive record that they do not understand, to preserve these 
fields and to write them out again whenever the descriptive record needs to be changed. 

More on type 1 records 


It is not always necessary for a type I record to have entries for each field defined in the field information 
record. Depending on the application, any trailing omitted fields will usually be assumed to be zero or 
null. 


It is also possible for a record to have more than 32 fields. This is allowed in the case where the 
descriptive record explicitly defines 32 fields. In this case, any extra data in a record, beyond the 32nd 
field, is interpreted as a sequence of additional string fields. 


The Series 3 Database 


In this section reference to the Series 3 Database is taken to include the Series 3a Database except where 
explicitly stated otherwise. 


The Series 3 Database stores its files in the DBF file format, as described in general terms at the 
beginning of this chapter. By convention, the Series 3 Database uses file with extension .dbf. 


Field information (type 2} record 
The field information record of a Series 3 Database file is always as follows: 
<20><20><03><03><03> ... <03><03><03> 


there being 32 <03>s in all. As mentioned above, this actually means that any type J record in the file 
consists of a variable arbitrary number of string fields (where this number can exceed or fall short of 32). 


48 


4 DBF FILES 


Extended header 


There are no extended headers on any Series 3 Database files. 


Descriptive (type 3) record 


For an example of a descriptive (type 3) record consider the following dump of a default newly-created 
and exited Series 3 Database .dbf file (see below for a Series 3a example): 


O: 4f 50 4c 44 61 74 61 62 61 73 65 46 69 Gc 65 00 OPLDatab aseFile. 
10: Of 10 16 00 Of 10 20 20 03 03 03 03 03 03 03 03 1... cee eee 
20: 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 nc cack cece ween 
30: 03 03 03 03 03 03 03 03 31 30 02 10 04 0002 50_~=(i........ . .. ht ee P 
40: 05 00 27 40 05 4e 61 6d 65 3a 07 05 20 48 6f 6d ..'d.Nam e:.. Hom 
50: 65 3a 07 05 20 57 6f 72 6b 3a 08 41 64 64 72 65 e:.. Wor k:.Addre 
60: 73 73 3a 00 06 4e 6f 74 65 73 3a ss:..Not es: 


The header of the first record after the field information record is <31><30>. This indicates that the record 
has type 3 and body length 0x31 bytes and therefore extends to the end of the file. 


The body of a Database descriptive record contains a series of fields, each of which has a header that 
gives its type and length, in the same format as the record header. A Series 3 descriptive record contains 
the following fields: 


® afield of type 1 and length 2. 
= a field of type 5 and length 2. 
= a field of type 4 and variable length; in the above example, the length is 0x27. 


Series 3a Database files are longer because the descriptive records contain more fields. Here is a newly- 
created and exited Series 3a Database .dbf file. 


QO: 4f 50 4c 44 61 74 61 62 61 73 65 46 69 6c 65 00 OPLDatab aseFile. 
10: Of 10 16 00 OF 10 20 20 03 03 03 03:03 03 03 03~—(iw.a 
20: 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 
30: 03 03 03 03 03 03 03 03 aa 30 02 10 04 00 02 50 
40: 14 00 3a 60 82 2e c6 41 08 07 08 07 72 20 b6 33 we cipeecA cecal 33 
50: dO 02 dO 02 00 00 00 00 01 00 ff ff 00 00 00 00 
60: 0 00 00 00 00 00 00 00 f0O 00 O02 00 00 00 00 00 


70: 00 00 00 00 00 00 f0 00 00 00 00 00 00 00 Od 70. ........ .... weep 
80: 00 52 4f 4d 3a 3a 42 4a 2e 57 44 52 00 Oc 80 00 «ROM::BuJ .WDR.... 
90: 00 00 25 50 00 82 2e c6& 41 08 07 Oc 90 25 50 00 oehPoces Aree APe 
a0: 82 2e c6 41 08 07 08 07 72 03 a0 01 00 01 04 bd SoA Mee” Mele e-c cee 

: 00 00 ff ff 2e 40 05 4e 61 6d 65 3a 07 05 20 48~—tiw.... @.N ame:.. H 


cO: 6f 6d 65 3a 07 05 20 57 6f 72 6b 3a 06 05 20 46 ome:.. Work:.. F 
dO: 61 78 3a 08 41 64 64 72 65 73 73 3a 00 06 4e 6f ax:.Addr ess:..No 
e0: 74 65 73 3a tes: 


The header of the first record after the field information record is <aa><30>. This indicates that the record 
has type 3 and body length Oxaa bytes and therefore extends to the end of the file. 


The body of a Series 3a Database descriptive record contains the three fields described above, together 
with the following additional fields: 


= a field of type 6 and length 58. 
= afield of type 7 and variable length. 
= afield of type 8 and variable length. 
= a field of type 9 and variable length. 
= a field of type 10 and length 3. 
= a field of type 11 and length 4. 
The currently used field types are as follows: 
type 1 the width of a tab, in columns. 


type 4 template data, containing leading-byte-counted text for the labels of 
consecutive lines of the display of a database entry (a line that has no label is 
represented by a single zero byte). 


49 


ADDITIONAL SYSTEM INFORMATION 


type 5 general flags options, described below. 

type 6 printer setup information, in the form of a PRINTER_PARAMS Structure. 

type 7 printer model: a zero terminated string that starts with the model number and 
continues with the full file specification of the printer driver file. 

type 8 text for the page header: a zero terminated string. 

type 9 text for the page footer: a zero terminated string. 

type 10 the diamond bar settings: bytes zero, one or two are non-zero if 'Find', 


‘Change’ or 'Add', respectively, are included in the diamond bar. 


type 11 the current search field data: the first two bytes specify the start field (with 
0x0000 meaning search in all fields), the second two bytes specify the end field 
(with Oxffff meaning search from the specified start field to the last field in the 
record. For example, values of 0x0010 and 0x0020 would imply a search from 
field sixteen to field thirty two inclusive. 


For further details of the content of fields of type 6, 7, 8 and 9, see Saving and restoring print context 
from file in the Printing chapter of the Object Oriented Programming Guide. 


Other descriptive record field types may be used by other versions of the Database application (eg types 2 
and 3 are used by the MC Database) and should be preserved intact when encountered. 


In the above example it can be seen that the width of the tab is equal to 4 columns, as given by the third 
and fourth bytes of the type J field that starts at offset 0x3b (remember that the first two bytes are the 
record header). The value of the flags options is 0x05, and the contents of the labels can easily be read as 
a sequence of leading byte-counted strings. 


Note that the first character in a template field name can be a telephone symbol (0x05) in which case the 
content of the corresponding field in any type 1 record can be used for automated dialling. 


Flags options for the Series 3 Database 
The meanings of possible bits in the general flags options (field type 5) are as follows: 


0x01 the permanent status window is on 
0x02 entries are word-wrapped 
0x04 a template should be displayed. 


All other bits are ignored and should, by default, be set to zero. 


Flags options for the Series 3a Database 
The type 5 field in a Series 3a Database descriptive record supports the following flags: 


0x01 this bit is ignored by the Series 3a Database 

0x02 entries are word-wrapped if this bit is set 

0x04 a template should be displayed if this bit is set 

0x08, 0x10 if both bits are clear there is no permanent status window; if bit 0x08 is set and 


bit 0x10 is clear a small status window is displayed; if bit 0x10 is set and bit 
0x08 is clear a large status window is displayed (bits 0x08 and 0x10 should not 
both be set) 


0x20, 0x40 these two bits record the zoom size of the text in the main display window 
according to the following table: 


0x00 Roman-11 
0x20 Roman-13 
0x40 Roman-16 
0x60 Roman-8 


All other bits are ignored and should, by default, be set to zero. 


Type 1 records 
Entries in the Database are stored as single type J records in the corresponding .dbf file. 


50 


4 DBF FILES 


The following special (non-text) characters may occur within any string in a type 1 record in a .dbf file, 
with these meanings: 


0x15 forced line feed (with the two part-paragraphs on either side of the forced line break just 
counting as one paragraph for the purposes of the template, even though each part word- 
wraps separately) 


0x05 prefixes a diallable telephone number (over and above any indication of diallability given in 
the template text) 


0x14 if this occurs as the first character in a string, it means this field should in fact be joined 
together with the preceding one, as discussed in the following section. 


Continuation sub-fields 


The Series 3 Database uses a special mechanism in order to store fields of text exceeding 255 characters 
in length. Any such fields are broken down into sub-fields with at most 255 characters of text: 


® the first 255 characters form the first sub-field 


= up to the next 254 characters form the second sub-field, with the special character 0x14 being 
placed at the beginning of the string (and included in the preceding byte count) 


= additional continuation sub-fields of up to 254 characters are used as required, in each case again 
being preceded by the 0x14 character. 


The continuation sub-field prefix character was in fact specially chosen so as to give a suggestive display 
on the MC, should the .ddf file be read into the MC Database application. 


5 a SS | 
The Series 3 Agenda 


This section describes only the Series 3 Agenda. The Series 3a Agenda is described in a separate chapter. 
The Series 3 Agenda stores its files in the DBF file format, as described in general terms at the beginning 
of this chapter. By convention, the Series 3 Agenda uses files with extension .agn. 
Field information record 
The field information record of a Series 3 Agenda file is always as follows: 

<05><20><00><00><00><00><03> 
indicating that each type 1 record in a .agn file consists of four integer fields followed by one string 
field. More details of these fields are given below. 
Extended header 


There are no extended headers on any Series 3 Agenda files. 


Descriptive record 


For an example of a descriptive record, consider the following, which is a dump of a default newly- 
created (and exited) Series 3 .agn file: 


O: 4f 50 4c 44 61 74 61 62 61 73 65 46 69 6c 65 00 OPLDatab aseFile. 
10: Of 10 16 00 Of 10 05 20 00 00 00 00 03 0e 30 Oc ww... wwe 0. 
20: f0 a4 01 3c 00 01 00 OF 00 1c 02 2e 00 seSecee. ome 


The header of the first record after the field information record is <0e><30>. This means that the record 
has type 3 and body length 0x0e bytes. Evidently, this record extends to the end of the file. 


The body of this descriptive record is made up of a single field of type 15 and length ox0c. The body of 
this single field consists of 6 integers, with the following meanings: 


= the time (in minutes since midnight) for the first appointment slot of the day 
= the default length of an appointment (in minutes) 
= whether or not alarms are on by default 


= the default advance time (in minutes) before a timed appointment for an alarm 


51 


ADDITIONAL SYSTEM INFORMATION 


= the default time (in minutes since midnight) for the alarm for an untimed appointment 
= the character (low byte only - the high byte is ignored) to be used as the time separator. 
Evidently, these sub-fields match the various lines in the Settings dialog within the Agenda application. 


Type 1 records 
Entries in an Agenda are stored as single type 1 records in the corresponding .agn file. 
Individual ToDo items and Repeated items are also stored as single type 1 records. 


The five fields in Agenda type 1 records are 


integer: DayNumber 
integer: Duration 
integer: Time 
integer: AlarmTime 
String: Text 


DayNumber is the number of days since 1/1/1900 (which is day zero). As far as the Series 3 Agenda is 
concerned, the first legal day is 1/1/1980, and the last legal day is 31/12/2049. 


Duration, Time, and AlarmTime are all stored in minutes (ignoring for the moment the fact that some 
calculations with these values have to be performed in places - as described below). 


The MSB (most significant bit) of Time is a flag that indicates whether the item is timed or untimed. If 
the MSB is set then the item is untimed. Accordingly, to extract the time from the Time field this value 
must be anded with 0x7fff to remove the MSB 


The LSB (least significant bit) of Duration is a flag that indicates whether the item has an alarm attached. 
If the LSB is set then no alarm is attached. Accordingly, to extract the duration from the Duration field, 
divide this value by 2 (thus removing the LSB and shifting down the duration). 


If the item is untimed then the Time field contains the day note slot number and its MSB must be set (ie 
or in 0x8000). In this case, the Duration field simply equals 0 if an alarm is attached or 0x01 if no alarm 
is attached. 


Note that the end time of a timed item (obtained by adding its start time and its duration) must always be 
less than 1440 (ie midnight). 
Calculating with AlarmTime 


For internal efficiency reasons, the way in which the alarm pre-time (as defined by the user) is stored in 
the .agn file is somewhat counter-intuitive. 


To recover the pre-time of the alarm for a timed appointment, the values of Time and AlarmTime for the 
entry should be added, and then the value (23*60+59) subtracted from this result. 


For example, if the value of Time is 0x3fc and the value of AlarmTime is 0x1b2, the actual alarm pre-time 
is 


Ox3fe + Ox1b2 - (23*60 + 59) 
ie 15 minutes (for an appointment actually at Spm in the afternoon). 


In the case of untimed appointments, the integer value obtained when AlarmTime is divided by 24*60 
gives the number of days previous to the appointment when the alarm is due. The time of day when the 
alarm is due is obtained by subtracting the remainder when AlarmTime is divided by 24*60, from 
23*60+59. 


For example, if the value of Time is 0x8000 and the value of AlarmTime is 0xe87, note that 
Oxe87 = 2*(24+60) + 839 
so that the alarm is due two days in advance of the appointment, at 10am (since 23*60+59-839=600). 


If required, appropriate values of AlarmTime to ensure given alarm pre-times can easily be calculated by 
reversing the above formulae. 


52 


4 DBF FILES 


Finally, note that if an appointment does not have an alarm set for it, the value of AlarmTime should be 
set to Oxffff. 

The text of an appointment 

The text for an appointment is contained within the Text field of the record. 


In all but the cases of repeated entries, the text is simply the entire contents of the field. For repeated 
items, the final six bytes of the Text field have a special meaning, as discussed below. 


In all cases, the length of the actual text for an appointment cannot exceed 63 characters. 


ToDo items 


ToDo items have a DayNumber of Oxf#ff, and must be stored as timed items (and so the MSB of the 
Time field must be clear). 


The Time field contains the item priority, that is a value from 1 to 9. 

The Duration field contains a secondary integer key, which orders the ToDo items within a given 
priority. 

ToDo items cannot have alarms attached. 

Repeat items 


Timed and untimed repeat items are stored in the same general way as normal timed and untimed items, 
except that they have a DayNumber of Oxfffe. 


The specific repeat details are stored in the six bytes at the end of the Text field and have the following 
format: 


Byte: Type 

Byte: Interval 

Integer: StartDayNumber 

Integer: EndDayNumber 
The Type field can take the following values: 

0 Repeat yearly 

1 Repeat monthly by date 

2 Repeat monthly by day 

3 Repeat weekly 

4 Repeat daily 

5 Repeat workdays. 


StartDayNumber and EndDayNumber are stored as the number of days since 1/1/1900 (which is day 0). 
Setting EndDayNumber to zero means the item repeats "forever". 


53 


CHAPTER 5 


SERIES 3A AGENDA FILE FORMAT 


Series 3a Agenda files are binary files containing typed variable length records. They use the same basic 
file and record structure as DBF files, but the file signature and internal record structure are NOT the 
same. 


In contrast, Series 3 Agenda files are DBF files and are described separately, in the DBF Files chapter. 


SESE SS SS a a a ae ae TT 
Basic structure of Agenda files 
The basic file structure for Agenda files is as follows: 


<standard header> Used to identify the file type and the version of the file structure. 
<extended header> For future use, currently omitted. 
<data records> The main body of the file, containing entries and preferences. 


The standard header 
The standard header is present at the start of all Agenda files and is always 32 bytes long. 


AGD_SIG_SIZE 16 
AGD_SPARE_SIZE 12 


typedef struct 
€ 
UBYTE sig (AGD_SIG_SIZE]; 
UWORD version; 
UWORD hSize; 
UBYTE spare[AGD_SPARE_SIZE]; 
> AGD_FILE_HEADER; 


The first 16 bytes of the file are always the zero terminated string "AgendafiteType*". This is used to 
identify the file as an Agenda file. 


The two byte parameter version, at file offset 0x0010, is the file version number, as described in the 
General System Services chapter of the Plib Reference manual. This is currently always 0x100F. The most 
significant nibble is the major version number; any change in this indicates that the file format may not 
be backwards compatible. 


The two byte parameter hSize gives the combined size of the standard and extended header. This 
effectively gives the file offset of the first data record within the file. Currently this is always 0x0020. 


The array spare is reserved for future use by Psion. 


The extended header 
At present this is never used and is reserved for future expansion. 


The data records 


As in DBF files, all data is stored as variable length, typed records, with the type and length combined 
into a single word (two bytes). The most significant nibble of the word gives the record type. The type 


55 


ADDITIONAL SYSTEM INFORMATION 


determines how the contents of the record are to be interpreted. The remainder of the word gives the 
length of the data that follows. This file structure is designed to be flash friendly in that deleted records 
are not normally removed from the file but are marked with record type 0, which can be done in place. 


This structure allows 16 record types 0x0 to Oxf, each of which are allowed to be up to Oxffe (4094) bytes 
long. Although a record length of Oxfff is not explicitly illegal, it is not used. 


SSS SS eee ee en eS ee ee ee) 
Record Types 


There are sixteen record types as follows. 


Type Record 

0 Deleted 

1 Appointments Gimed day entries) 
2 Day notes (un-timed day entries) 
3 Anniversaries 

4 To-do entries 

5 Repeat records 

6 Anonymous data 

7 Reserved 

8 Reserved 

9 To-do list information 

10 Descriptive records 1 

11 Descriptive records 2 

12 Descriptive records 3 

13 Descriptive records 4 

14 Descriptive records 5 

15 Illegal (used to mark a write failure) 


Currently record types 6, 7 & 8 are never generated by the Series 3a Agenda. 


A number of the records contain day numbers and times. Unless stated otherwise all dates are given as a 
daynum. A daynum is the number of days from 1 Jan. 1970. For technical reasons dates before 1 Jan. 
1980 (daynum 3652) or after 31 Dec. 2049 (daynum 29219) are ignored by the Agenda and where 
appropriate will be ‘clipped’ to one or other of these dates (for example a repeating entry that starts on 10 
June 1970 will have its start date 'clipped' to 1 Jan. 1980). 


aS 6 ee eee oe ee ae 
Type O - deleted record 


As for DBF files, deleted records are not normally removed from the file but have their record type 
changed to type 0. As any of the above record types may be converted to a type 0 record, there is nothing 
that can usefully be said about the contents of such a record (indeed the record may not even have been a 
valid Agenda record before it was deleted). Records of this type should be ignored except to calculate the 
amount of space that would be freed by compressing the file. 


There may be any number of such records in the file. 


56 


5 SERIES 3A AGENDA FILE FORMAT 


ESE SSS SS SS ee ee ee ee eS a 
Types 1 to 4 - entry records 


Records of type 1 to 4 contain details of individual Agenda entries. Each has the same conceptual 
structure as follows: 


<Entry details field> This is dependent on the entry type. 

<Title field> Obligatory, variable length field containing the text of the entry. 
<Alarm field> Optional, fixed length field containing alarm time and sound. 
<Memo field> Optional variable length field containing any memo for the entry. 


The record type is used to determine the length and meaning of the first field. Although these have some 
similarities all four are individually described in details below. 


coe 


AIK 


This field contains most of the non-textual information describing when an entry occurs, what other 
fields it has and various bits of type specific information, such as the duration for timed appointments. 


Type 1 (timed day entry/appointment) 


A type 1 record stores details of entries that occur at a specific time on a specific day. In the Series 3a 
Agenda these are called timed day entries. 


The details field for a timed day entry consists of eight bytes structured as follows: 


UWORD day; 
UWORD time; 
UBYTE attr; 
UBYTE code; 


UWORD dur; 

day is the daynur of the day on which the entry appears. 

time is the time of the start of the appointment in minutes from midnight. 

attr is a byte containing flags for attributes the entry may or may not have (see 
below). 

code is the ASCII character code for the symbol that is to be associated with the 
entry when it is visible in the Year view. Values less than 32 are ignored and 
treated as if the entry should not appear in the Year view. 

dur is the duration of the appointment measured in minutes. It is constrained such 


that the appointment cannot end after 11:59 PM. i.e. this field is between 0 
and 1439 - time (inclusive). 


Type 2 (untimed day entry/note) 


A type 2 record stores details of entries that appear on a specific day but do not have a time associated 
with them. In the Series 3a Agenda these are called untimed day entries. 


The details field for an untimed day entry consists of six bytes structured as follows: 


UWORD day; 
UWORD slot; 
UBYTE attr; 
UBYTE code; 


day is the daynum of the day on which the entry appears. 


stot is the time slot (in minutes from midnight) in which the entry will appear in 
the Day and Week views. For example a stot value of 780 would show the 
entry at the start of the 1pm slot. If this is oxffff then the entry will appear in 
the default slot. 


attr is the attributes byte (see below). 


57 


ADDITIONAL SYSTEM INFORMATION 


code is the ASCII character code for the symbol that is to be associated with the 
entry when it is visible in the Year view. Values less than 32 are ignored and 
treated as if the entry should not appear in the Year view. 


Type 3 details (anniversaries) 


A type 3 record stores details of anniversaries (entries which appear in the Anniversary view). Although 
these are usually repeated there are cases where a single anniversary entry will exist. For details of 
repeated entries see type 5 records below. 


The details field for an anniversary record consists of nine bytes structured as follows: 


UWORD day; 

UWORD slot; 
UBYTE attr; 
UBYTE code; 
UWORD baseYear; 
UBYTE displayAs; 


day is the daynum for the day the anniversary entry will appear on. 


slot is the time slot (in minutes from midnight) in which the entry will appear in 
the Day and Week views. For example a slot value of 780 would show the 
entry at the start of the Ipm slot. If this is oxffff then the anniversary will 


appear in the default slot. 
attr is the attributes byte (see below). 
code is the ASCII code for the character to display when the entry is visible in the 


Year view. Values less than 32 are illegal and cause the entry not to appear in 
the Year view. 


baseYear is the year of the event that the anniversary commemorates. Positive values 
indicate AD years e.g. 55 means 55 AD and negative values indicate BC e.g. 
-5 means 5 BC. A value of zero indicates that there is no baseYear. The 
allowed range is from 30000 BC to 2049 AD 


displayAs contains flags detailing how the entry is to be displayed. The flags are as 
follows: 0x01 for baseYear displayed, 0x02 for elapsed years displayed, 0x03 
for both of the preceding options and 0x00 for none of them. 


Type 4 details (To-do) 
A type 4 record holds details of to-do entries. 


These are entries which usually have an associated due date and a display from date. They appear in the 
corresponding to-do list, and depending on the preference settings will appear in the Day view from the 
displayFrom date until they are crossed out or deleted. 


The details field for a to-do entry consists of 14 bytes structured as follows: 


UWORD displayFrom; 
UWORD slot; 

UBYTE attr; 

UBYTE code; 

UWORD dueDate; 
UBYTE listNo; 
UBYTE priDisp; 
ULONG order; 


displayFrom the daynum of the day on which the entry first appears in the Day/Week views 
of the Agenda. This must normally be the same or less than the dueDate value. 
If the entry is crossed out (see the description of the attr byte below) this is 
the daynum of the day on which the entry was crossed out. In this case, and 
only in this case, the displayFrom can be later than the dueDate. 
If the disptayFrom daynum is Oxffff, then this is an un-dated to-do i.e. one that 
always appears on today. In this case the dueDate will also be Oxffff. 


slot the time slot (in minutes from midnight) in which the entry will appear in the 
Day and Week views. If this is oxffff then the entry will appear in the default 
slot of the appropriate to-do list. 


58 


5 SERIES 3A AGENDA FILE FORMAT 


attr the attributes byte (see below). 


code the ASCII code for the character to display when the entry is visible in the 
Year view. Values less than 32 are illegal and cause the entry not to appear in 
the Year view. 


dueDate the daynum of the day the to-do should be done by. If this value is oxffff the 
record is an undated to-do (i.e. one which always appears on today when it 
appears in the Day/Week views). 

ListNo the ID (in the range 0 to 255 inclusive) of the to-do list in which the entry will 


appear. Note that a value of zero does nor necessarily mean that the entry 
appears on the first to-do list in the To-do view - the order is determined by 
the contents of a type 11 record. See the description of type 9 records for 
further details of this ID. 


pridisp this byte consists of two nibbles that give the priority and the method of 
displaying the to-do. 
The least significant nibble (bottom four bits) has a value one less than the 
priority of the to-do. Thus a priority one to-do has value zero, priority two has 
value one etc. This will currently always be in the range zero to eight inclusive 
and all other values are illegal. 
The most significant nibble (top four bits), determines how the due date should 
be displayed. There are currently four legal values: 
0 - Automatic, shown as a date until within one week of the due date, then 

shown as, for example, "Next Wed". 

1 - Always shown as a date. 
2 - Shown as the number of days until the due date. 
3 - The due date is never shown. 


order this determines the position of the entry in its to-do list when the list is 
displayed in manual order. This field is not assigned consecutively. Thus a 
value of 3 for example in this field does not necessarily mean that the entry 
appears third (or fourth) on the to-do list. 


The attributes byte ‘aftr’. 


The above record types (1 to 4) contain an attributes byte as the fifth byte within the record data. This 
byte contains flags which indicate which of the two optional fields are present in the record, whether or 
not the entry is repeated etc. The following flags are currently defined for the attributes byte (all other 
bits should be 0). 


Bit Meaning 


00000001 Once only: if this bit is set the entry appears only once in the Agenda. If it is 
clear the entry is repeating and there will be an associated type 5 repeat record 
elsewhere in the file. 


00000010 Pending: this bit is set if the entry has not been crossed out. If it is clear the 
entry has been crossed out. In the case of to-do entries this alters the meaning 
of the displayFrom field of the details. 


00000100 Display code: if this bit is set the entry should be displayed in the Year view 
(providing it has a legal code field): if it is clear the entry should not appear in 
the Year view, regardless of the value of the code field. 


00001000 No alarm: this bit is set if the entry does not have an associated alarm in which 
case there will be no alarm field at the end of the record. If it is clear there is 
an alarm field immediately after the title field. 


00010000 No memo: this bit is set if there is no memo associated with the entry, in 
which case there will be no memo field at the end of the record. 


The wie Geld 
The title field must be present for entry records of all four types. This field follows immediately after the 
details field and contains the text for the entry as well as flag for how that text should be displayed e.g. 
bold, italic etc. 


The format of the title field is: 


59 


ADDITIONAL SYSTEM INFORMATION 


UBYTE style; 


UBYTE len; 
title text. 
style contains flags indicating how the text should appear. The flags are as follows: 
0x01 for bold, 0x02 for underline, 0x20 for italic. 
len is the length of the text comprising the title, and the number of bytes 
following. This may take any value from 0 - 254. 
title text is len bytes of the title. Note that this text is not zero terminated. 


This fixed length field is only present if the attributes byte does not have bit 3 (0x08) set, i.e. there is an 
alarm set for the entry. 


When present this entry immediately follows the title field and has the following format: 


UWORD preTime; 
UBYTE len; 
UBYTE sound [8]; 


preTime is the time at which the alarm should occur given in minutes before 11.59 on 
the day the entry appears on. (In the case of a type-4 to-do record this is the 
due date). This field can be between 0 and 46079 (midnight before, 31 days 


" before the entry). 

len this gives the length of the text in the sound element. 

sound is always eight bytes long the first ten of which contain the name of the WVE 
file for the alarm. A number of WVE files having names of the form 
SYS$ALnn are built-in to the ROM. In addition name can be set to an ASCII 
character with value between 1 and 16 inclusive: currently only "\0x01", 
"\0x02" and "\0x10" are used corresponding to the rings, chimes and silent 
alarms. If the name of the file is less than eight bytes long, all remaining bytes 
should be zero. 


This variable length field is only present if the attributes byte (in the details field) does not have bit 4 
(0x10) set, i.e. there is a memo attached to the entry. 


When present this field comes at the end of the record, immediately after the alarm field if there is one, 
and after the title field if there is not. The field has the following format: 


UWORD dataLength; 
UWORD Leni; 

UWORD len2; 

UBYTE data [I]; 
UBYTE data2[]; 


dataLength is the length (in bytes) of the data in the memo field. This can be between 0 
and 3600 (inclusive) and includes two separate blocks of data. 

tent the length of the first block of data, with the two most significant bits used for 
flags 

len2 the length of the second block of data 

data contains len1&0x3fff bytes of data. If teni has the bit 0x4000 set, the first ten 


bytes contain options data, as described for the options data (type 1) record in 
the Word Processor Files Format chapter, otherwise a default set of options 
(the word processor default options) is used. If ten1 has the bit 0x8000 set, the 
memo is password-protected. In this case the next eighteen bytes contain 
password-matching information and the following data is encrypted. The 
remaining bytes contain the plain text of the memo, with each paragraph 
terminated by a zero byte. As described for the word processor file format, the 
data does not include the final zero terminator. 


60 


5 SERIES 3A AGENDA FILE FORMAT 


data2 len2 bytes of data containing the document index for the memo. This is in 
exactly the same format as described in the Word Processor File Format 
chapter for the document index (type 9) record. 


EEE ee ee eee ee ee ea 
Type 5 - repeats 


When an entry is set to repeat in the Agenda, a second record is written to the file in addition to the type 
1 - 4 entry record. This record contains the date to repeat until, the days to repeat on and a list of those 
dates for which the repeat should be suppressed. 


Repeat records are always paired with an entry record which has bit 0 (0x01) of the attributes byte clear. 
Because of the way the Series3a Agenda writes the file a type 5 repeat record will always occur after the 
associated entry record, but this is not necessary and there is no reason why it should not occur earlier in 
the file. If a repeat record is missing its associated entry record or if a repeated entry record exists for 
which there is no associated repeat record, then the record in the file will be ignored. 


The repeat record is structured as follows: 


UBYTE alg; 

UBYTE ival; 

UWORD endDate; 
UBYTE type; 

UBYTE tags[n]; 
ULONG filePos; 
UWORD exceptions []; 


alg is the repeat algorithm and various flags. The bottom three bits of this byte 
may take one of the following values: 0 - repeat daily, 1 - repeat weekly, 2 - 
monthly by date, 3 - monthly by days, 4 - repeat annually. 
If bit 3 (0x08), of this byte is set, the repeat should only appear once in the 
dated views on the first occurrence after today. Otherwise all valid occurrences 
are shown. All other bits in this byte should be 0. 


ival is the daily repeat interval. This is zero if the entry repeats daily, 1 if the entry 
repeats every other day etc. A value of 255 is not valid. 


endDate this is the daynum of the last day on which the entry can repeat. This is not 
necessarily the last day on which it will appear. The start date is taken from 
the day value at the start of the details field in the entry record. In the case of a 
repeating to-do this is the displayFrom field that is normally used to give the 
date from which to display the entry. Here it is used to determine the start date 
of the repeat algorithm and hence determine which due dates will be associated 
with the todos. To work out the display from dates for each instance of the 
repeated to-do use the dueDate-displayFrom in the entry record to determine the 
number of days warning for each instance of the repeat. 


Note that the Agenda does not support anniversaries that repeat for limited 
periods. In consequence, it is recommended (but not required) that a repeat 
record for an anniversary (type 3) record should have an endDate set to the 
value Oxffff. 


type is the type (1-4) of the associated entry record. The Agenda requires this 
information to be present, even though it is technically redundant (since it can 
be determined from the associated record itself). 


61 


ADDITIONAL SYSTEM INFORMATION 


tags is n bytes that determine which days occur in the repeat sequence. The number 
of bytes n and their meaning is determined by the repeat algorithm. For repeat 
daily/annually there are no tag bytes since the start date and ival determine 
which dates to repeat over. 
For weekly repeats there are two tag bytes. The first has a bit set for each day 
of the week on which the repeat occurs. Bit 0 for Monday, bit 1 for Tuesday 
etc. Bit 8 is not used and should always be 0. The second byte determines 
which is the first day of the week, 0 for Monday, 1 for Tuesday etc. This is 
significant when the repeat does not occur every week. A repeat which occurs 
on say, Tuesday and Thursday of every other week, occurs on different dates if 
the week starts on Monday than it does if the week starts on Wednesday. 
For monthly by date repeats there are four tag bytes. Each bit in these bytes 
represents a different day of the month. Bit 0 in the first byte is the ist of the 
month, bit 1 the second and bit 7 the eighth of the month. Bit 0 of the second 
tag byte is set if the repeat occurs on the 9th and so on. Bit 7 of the fourth byte 
is not used and should be 0. 
Monthly by days repeats have five tag bytes. Bytes 0 to 3 correspond to the 
first, second, third and fourth occurrences of each day, for example bit 1 of 
byte 2 is set if the algorithm repeats on the 3rd Tuesday of each month. The 
last tag byte contains bits set if the repeat should occur on the last Monday, 
say, of the month. 


filePos is the offset from the start of the file at which the corresponding entry record 
can be found. This is given in bytes from the start of the file (not the start of 
the data records), and gives the position of the type length word at the start of 
the record. Note that this mechanism of associating repeat records with the 
underlying entry relies on the deleted entries not being removed from the file: 
they are just marked with the deleted record type. Removing such records 
would require all FitePos fields to be recalculated. 


exceptions The remainder of the record consists of words, each of which is the daynum of 
a day on which the repeat should be suppressed. The number of exceptions is 
determined by the length of the record. Although the Series 3a Agenda 
currently always writes these exceptions in strictly increasing order, there is no 
guarantee that this will always be the case. Any illegal values (either outside 
the valid range for the Agenda or on a day which is not normally a repeat 
instance) should be ignored but preserved. 


Type 6 - anonymous data 


This record type is currently not used and is set aside for storing non-displayable text carried with the 
Agenda. It is intended that this information will be used by, say, conversion programs that convert other 
Agenda file formats to that of the Series 3a. This allows the file to be converted back without losing 
information from the original file. These records are ignored by the series 3a engine except when 
merging files. In this case incoming anonymous data records are added to the file into which data is being 
merged. 


Types 7 and 8 - reserved 


These record types are reserved for future expansion and should not be used. 


Type 9 - to-do list information 


There is one record of this type for each to-do list in the Agenda. Each contains the settings for the to-do 
list and the to-do list number (as in type 4 to-do records) that corresponds to the list. 


The format for these records is as follows: 


5 SERIES 3A AGENDA FILE FORMAT 


UBYTE sig; 

UBYTE cat; 

TEXT name[16+1}; 
UBYTE catid; 
UWORD flags; 
UWORD vis_slot; 
UBYTE advance_days; 
UBYTE yearcode; 
AGPREF_ALARM a; 
UBYTE style; 
UBYTE spare; 


sig a signature byte which determines the format of the rest of the record. 
Currently the only legal value is Oxff 


cat the ID, in the range 0 to 255 inclusive,! of the to-do list to which the type 9 
record refers. The settings apply to all to-do entries (type 4 records) that have 
a value of the ListNo field that is equal to the value of cat. See the description 
of the type 11 record for the order of appearance of to-do lists in the To-do 


view. 

name [1 the name of the to-do list, containing up to 16 characters. The remaining bytes 
of name] are all NULL 

catid the to-do list ID, as in cat, repeated for reasons of coding convenience 

flags the contents of this field are described below 

vis_slot the default position in the Day view, in minutes from midnight 


advance_days 


the default number of days advance warning for alarms. The value is retained 


even when the list is not dated 


yearcode the ASCII code of the character used in the year view. This character is always 
specified, even if the to-do list is not visible in other views 


a an AGPREF_ALARM struct that specifies any untimed alarm. See the explanation of 
this struct in the later description of the General Descriptive (type 13) record 


style the default style for entries. Its value is either G_STY_NORMAL, or any 
combination of G_STY_BOLD, G_STY_ITALIC and G_STY_UNDERLINE 


spare this field is not currently used in Agenda files, but should be set to zero. It is 
reserved for use by future file versions. 


The flags field 


This field contains, in its least significant four bits, a value between one and nine inclusive. This 
specifies the lowest priority of item that will be displayed in other views. 


The two flag values 0x0010 and 0x0020 are mutually exclusive and determine the order in which items are 
listed. If bit 0x0020 is set, the items in the to-do list are listed in a user-specified order. Alternatively, if 
bit 0x0010 is set, the items are ordered by priority, with items of the same priority listed by date. If 
neither bit is set, items are listed in date order, with items of the same date listed by priority. 


If bit 0x0040 is set, crossed-out entries are shown in the list. 
If bit 0x0080 is set, crossed-out entries are shown in other views, provided that bit 0x0200 is also set. 


If bit 0x0100 is set, the entries are displayed with sequentially numbered bullets, otherwise they are 
bulleted with their priorities. 


Setting bit 0x0200 specifies that the entries are displayed in other views. 
Setting bit 0x0400 specifies that entries are dated by default. 


Bit 0x0800 is used internally by the Agenda application and may be set or clear in a type 9 record. It is 
ignored when the record is read. 


1 There is no need for to-do list IDs to be consecutive. An ID can have any value up to, and including, 
255. However, a to-do list ID generated from within the Agenda application will always lie within the 
range O to 98 inclusive. 


63 


ADDITIONAL SYSTEM INFORMATION 


The most significant four bits are not used, and should be set to zero. ( 


ee ea et et ee eee ee ey eee 
Types 10 to 14 - descriptive records 


Records of types 10 to 14 inclusive contain the preferences settings for the Agenda (these may be set via 
the preferences dialog). Only one record of each type is allowed per Agenda file. If there is more than 
one record of any type, only the last record of each type is significant. 


A descriptive record consists of the usual header word followed by the record body. The record body 
contains either simple data or a set of type/length/value (TLV) fields. A TLV field consists of a one word 
header followed by the field body. The top four bits of the field header contain the field type. The 
bottom 12 bits contain the length of the field body. No more than one of each of the specified fields can 
be present in a given record. 


The structure of these records should not be extended (extra TLV fields should not added even though 
these would be ignored). 


Type 10 to 14 records are described in greater detail below. 


== SSeS ee ee ey ( 
Type 10 - styles descriptive record 


The styles descriptive record contains the styles and emphases that are used by the Memo editor for all 
memos in the file. 


This record is written when a memo has been created for the first time, and thereafter whenever its 
content has changed. 


The body of this record contains the following: 


UWORD stylen; 

UWORD emphlen; 

UBYTE stydata[stylen]; 
UBYTE emphdatalemphlenj; 


stylen the total length of the following style data 
emphLen the total length of the following emphasis data 
stydata styten bytes of data, consisting of one or more styles. Each style occupies 80 


bytes and is of the same structure as described in the Word Processor File 
Format chapter for the body of the word processor style data (type 6) record 


emphdata emphlen bytes of data, consisting of one or more emphases. Each emphasis 
occupies 28 bytes and is of the same structure as described in the Word 
Processor File Format chapter for the body of the word processor emphasis 
data (type 7) record 


SS Se Se en a re 
Type 11 - to-do manager descriptive record 


This record is written on creation of a new Agenda and so is always present. It gets rewritten whenever 
changes have been made. 


This record contains the information that the Agenda needs to determine which position each to-do list 
has in the To-do view. The body of the record consists of three or more bytes, structured as follows: 


UBYTE sig; 
UBYTE ncats; 
UBYTE catid[ncats] 


sig is the signature and is always Oxéc. 


neats is the number of categories, and must not exceed 99. 


5 SERIES 3A AGENDA FILE FORMAT 


catidincats] determines the order of appearance of to-do lists in the To-do view. The array 
contains the IDs, in display order, of all the to-do lists in the Agenda file. 
Thus catidt0] holds the ID of the first displayed list, catid(1] the ID of the 
second, and so on, up to a maximum of catid[98}. 


Type 12 - frequently changing data descriptive record 


This record is written on creation of a new Agenda and so is always present. However, as it contains data 
that changes frequently, it is only rewritten when the file is closed. 


This record contains zoom, wrap and status window settings for each view. The body consists of a TLV 
header word (type zero, length 18) followed by six viEw_SCREEN_CFG structures for the Day, Week, Year, 
To-do, Anniversary and List views respectively. A VIEW_SCREEN_CFG structure consists of three bytes and 
is structured as follows: 


UBYTE statmode; 
UBYTE wrapmode; 
UBYTE zoom; 


statmode this field is a flag for the status window. It is 0 if a status window is not 
visible, 1 if the small status window is visible and 2 if the large status window 
is visible. This flag applies to all views. 


wrapmode in all views except the Year view the wrapmode field is 1 if wrap is on, and is 
otherwise 0. In the Year view (which does not use wrapping) the wrapmode field 
contains the index of the month that is displayed in the first row of the planner 
(0 = January, 11 = December). 


zoom this field takes the value 0 to 3 corresponding to Roman fonts of height 8, 11, 
13 and 16 respectively. It is relevant for the Day, Week, To-do, Anniversary 
and List views. The zoom field is unused in the Year view entry and is set to 0. 


Type 13 - general descriptive record 


This record is written on creation of a new Agenda and so is always present. It stores data shown in the 
dialogs under the Preferences menu. It gets written whenever the values shown in one of these dialogs are 
changed. 


This record contains entry defaults and all individual view preferences (to-do entry defaults are stored in 
to-do list records). Each record has the normal Agenda type/length header followed by one or more TLV 
fields. There are sixteen possible type fields only some of which are currently used: the rest are reserved 
for future use. If a field is not found the Agenda will use the corresponding default values. The fields can 
be in any order. 


Type Field 

4 Diamond list setup 
5 Day entry defaults 

6 Anniversary defaults 
7 General defaults 

8 Day view settings 

9 Week view settings 
10 Year view settings 
11 To-do view settings 
12 Anniversary view settings 
13 List view settings 

14 


Diamond list setup field = 


The diamond list setup field indicates which views should be included in the diamond list. It consists of 
six bytes structured as follows: 


UBYTE fnbar [6] ; 


65 


ADDITIONAL SYSTEM INFORMATION 


fnbar contains six bytes corresponding to the Day, Week, Year, To-do, List and 
Anniversary views respectively. Each byte is either TRUE or FALSE depending on 
whether or not the view is to be included in the diamond list. 


The day entry defaults field contains the defaults for entries in the Day view. The field consists of 38 
bytes structured as follows: 


UWORD DefUntimedEntViewT ime; 
UWORD DefTimedEntT ime; 

UWORD DefTimedEntDuration; 
UBYTE DefTimedByDefault; 
UBYTE DefYearSym; 
AGPREF_ALARM UntimedAlarmefs; 
AGPREF_ALARM TimedAlarmefs; 
UBYTE style; 

UBYTE spare; 


DefUntimedEntViewTime is the default display time for untimed entries in minutes since 00:00. 


DefT imedEntT ime is the default display time for timed entries in minutes since 00:00. 

DefT imedEntDuration is the default duration for a timed entry in minutes. 

DefT imedByDefault is 1 if entries are timed by default, otherwise it is 0. 

DefYearSym is the character code for the default year symbol. 

Unt imedAlarmDefs contains details of the default alarm for untimed entries (see below for a 


description of the AGPREF_ALARM structure). 


TimedA Larmefs contains details of the default alarm for timed entries (see below for a 
description of the AGPREF_ALARM structure). 


style is the style of the font used for the entry. It should be a suitable ored 
combination of the Wserv c_sTy_xxx flags: 0x00 for normal, 0x01 for bold, 0x02 
for underline and 0x20 for italics. 


spare is reserved and is set to 0. 
An AGPREF_ALARM structure contains default alarm details. It consists of 14 bytes structured as follows: 


UBYTE on; 
UBYTE ndays; 
UWORD minutes; 
SE_SND snd; 


on is 1 if an entry has an alarm by default and 0 otherwise. 


minutes for timed entries minutes is the time interval in minutes between the alarm 
going off and the start of the entry. For untimed entries minutes is the default 
alarm time in minutes from midnight. 


ndays for timed entries ndays is unused but should be set to 0. For untimed entries 
ndays is the default number of days between the alarm going off and the start 
of the entry. 

snd contains details of the default alarm sound (see below for a description of the 


SE_SND structure). 


The SE_SND structure contains details of the default alarm sound. It consists of ten bytes structured as 
follows: 


UBYTE len; 
TEXT name [8] ; 
UBYTE zero_term; 


Len is the length of the name of the alarm. 


name contains the name of the alarm stored as a sequence of len characters. When 
len is less than eight the first unused byte contains a NULL character. Any 
remaining unused bytes can take any value. 


66 


5 SERIES 3A AGENDA FILE FORMAT 


zero_term contains the NULL character. 


An anniversary entry defaults field contains the defaults for entries in the Anniversary view. It consists of 
20 bytes structured as follows: 


UWORD DefEntViewT ime; 
UBYTE AutoApplyYearSym; 
UBYTE DefYearSym; 
AGPREF_ALARM AlarmDefs; 
UBYTE style; 

UBYTE spare; 


DefEntViewT ime is the default display time for anniversaries in minutes since midnight. 
AutoApplyYearSym is 1 if the year symbol is on by default, otherwise it is 0. 

DefYearSym is the character code of the default year symbol. 

Alarmefs contains the default alarm details for timed and untimed entries (see the Day 


entry defaults field for details of the AGPREF_ALARM structure). 


style is the style of the font used for the entry. It should be a suitable ored 
combination of the Wserv G_sty_xxx flags: 0x00 for normal, 0x01 for bold 0x02 
for underline and 0x20 for italics. 


spare is reserved and is set to 0. 


UBYTE p_psion_enter; 
UBYTE timesep; 


p_psion_enter is set to TRUE if PSION+ENTER is used to complete an entry without going into 
the Entry details dialog. Otherwise p_psion_enter is FALSE. 


timesep is the character code for the Agenda time separator character. 


UWORD agnv_flags; 
ADENTVU_PREF left; 
ADENTVU_PREF right; 


agnv_flags this field contains both general view flags and Day view flags (all bits not used 
are reserved). The general view flags are as follows: 
0x01 for show appointment duration (Day, List, Week and Year views), 
0x02 for show appointment end time (Day, List, Week and Year views only), 
0x100 for show untimed day notes (Day, List and Week views only), 
0x200 for show anniversaries (Day, List and Week views only), 
0x400 for show to-dos (Day, List and Week views only) and 
0x800 for show timed day notes (Day, List and Week views only). 
The Day view flags are as follows: 
0x04 for title to go on right hand side, 
0x08 for slot compression off, 
0x10 for duration arrows off and 
0x20 for show overlap bars off. 


teft contains the preferences for the left hand side of the Day view (see below for a 
description of the ADENTVU_PREF structure). 


right contains the preferences for the right hand side of the Day view (see below for 
a description of the ADENTVU_PREF structure). 


An ADENTVU_PREF structure consists of 12 bytes structured as follows: 


67 


ADDITIONAL SYSTEM INFORMATION 


UWORD flags; 
UWORD begintime; 
UWORD beginvis; 
UWORD endvis; 
UWORD endtime; 
UWORD slotdur; 


flags oo a combination of the slot lines on flag (0x01) and the slot times on flag 
x . 

begint ime is O for the left hand side and beginvis for the right hand side. 

beginvis is the start time of the first slot in minutes from midnight. 

endvis is the start time of the last slot in minutes from midnight. 

endt ime is right.beginvis for the left hand side and 1440 for the right hand side. 

slotdur is the slot duration in minutes. 


The Week view settings field consists of two bytes as follows: 
UWORD pref_flags; 


pref_flags contains both general view flags (see above under the Day view settings field), 
and Week view flags. Currently there is only one Week view flag: 0x4 for 
show title on right hand side. 


The Year view settings field consists of two bytes as follows: 


UWORD pref_flags; 


pref_flags contains a combination of the general flags (see under the Day view settings 
field above) for showing appointment duration and/or end time or neither. 


The To-do view settings field consists of two bytes structured as follows: 
UWORD ncols; 


ncols contains the number of columns to be shown in the To-do view. The value of 
ncols should not be more than the number of existing categories. 


The Anniversary view settings field consists of two bytes structured as follows: 


UWORD ncols; 


ncols contains the number of columns to be shown in the Anniversary view (1 to 4). 


The List view settings field consists of two bytes structured as follows: 
UWORD pref_flags; 


pref_flags contains a combination of general view and Day view flags (see under the Day 
view settings field above) and the show-repeats-once flag (0x04). 


68 


3 SERIES 3A AGENDA FILE FORMAT 


—SSS SSS ee ee ee ee ee 
Type 14 - print setup descriptive record 


This record does not initially exist. It is created/rewritten to contain a new copy of the data for the 
Agenda print setup after using the Agenda Print setup dialog. It is also created/rewritten after a memo 
has been created or edited and Print setup data has been changed. Hence it is possible that the record will 
only contain Agenda Print setup data or Memo Print setup data. 


The record consists of a one word header followed by a series of TLV fields. 

Field types 0 to 3 and 6 to 9 are allowed. Field types 0 to 3 are used for the main Agenda print setup. 
Field types 6 to 9 are identical to field types 0 to 3 and are used for the memo print setup. 

Field type O and 6 

Contains printer parameters in a PRINTER_PARAMS structure as returned by the PR_GET_PARAMS method of the 
printer active object of the FORM dyl. 

Field types 1 and 7 

Contain printer model data from the PR_SENSE_MODEL method of the printer active object. The first byte is 
the model number as returned by PR_SENSE_MODEL, followed by the name, up to and including the NULL. 
Field types 2 and 8 

Contain printer header text from the PR_GET_HD method of the printer active object including the 
terminating NULL. 

Field types 3 and 9 


Contain printer footer text from the pR_GET_HD method of the printer active object including the 
terminating NULL. 


LSS SSS SS ee ee ee ae ee ae) 
Type 15 - illegal 


This record type is illegal and is used to protect the Agenda against write failures on a flash SSD. 
Writing to a flash SSD can fail at any time due to, say, a low battery. To protect as much as possible 
against this the Agenda will write the whole of the rest of the record before 'blowing down' the byte 
containing the record type to its correct value. This means that any file which contains a record with type 
15 (Oxf) has suffered a write failure and data beyond this point cannot be trusted. 


This record type is probably best thought of as an End Of File record, and any program finding a file 
with a type 15 record should start by setting the end of the file to the start (the type length word) of the 
record. 


CHAPTER 6 


WORD PROCESSOR FILE FORMAT 


This document describes the structure of a Psion word processor document file. The description to a level 
that allows other software to read and write non-password-protected document files. 


Psion word processor document files contain a file header, followed by a number of type-length-value 
records, from the following list: 


= options data 

= printer-related data 
= printer model data 
= page header 

= page footer 

= style data 

= emphasis data 

= document text 

® document index 


Document files created by the word processor will contain records in the order listed above. Each record 
consists of: 


# atwo-byte record type 
= atwo-byte record length, len 
= Len bytes of data 


Unless stated otherwise, all dimensions are stored in twips (1/1440 inch). 


Reserved locations 


All locations reserved for future use contain a value of zero. 


ee ee ee ae aR a eS a a, 
The document header 


The document header is 40 bytes long. It starts with the 16-byte zero terminated file signature 
"PSIONWPDATAFILE” followed by a two byte file version number. 


Following this is a 20-byte struct, containing password data. For a non-password-protected document 
there are two bytes of zero followed by eighteen bytes, each containing OxeEA. 


The remaining two bytes of the header are reserved for future use. 


71 


ADDITIONAL SYSTEM INFORMATION 


3 == ae en ee a ee 
Record types 


The options data record contains the following items: 


UWORD cpos the saved cursor position 
UBYTE symbols specifies the visibility of screen symbols, as indicated below 
UBYTE backup/statzoom on the MC, a non-zero value indicates that backup files are to be maintained 


on the Series 3a this byte stores the current status window and zoom states: the 
top four bits indicate the current zoom state (0x22 by default) and the lowest 
four bits indicate the status window size: 0, 1 or 2 for off, small or big 
respectively 


this byte is not used on the Series 3 
UBYTE style TRUE to show style bar at the left of the text 


for the OPL program editor, a TRUE value sets the use of a bold typeface 


UBYTE mono TRUE to load text by line, else by paragraph 
’ for the OPL program editor, a TRUE value sets the use of a monospaced 
typeface 
UBYTE outlevel lowest outline level to display 


not used for the OPL program editor 


UBYTE spare! reserved for future use, but may contain the OPL program editor data as 
follows: 


used by the OPL program editor, where a TRUE value specifies the use of auto 
indentation. Set to TRUE by default 


UWORD spare2 reserved for future use, but may contain the OPL program editor data as 
follows: 
used by the OPL program editor, to specify the column spacing for tabs. Set to 
2 by default 

Displayed screen symbols are determined by any combination of the following bit fields in symbols: 

0x01 show tabs 

0x02 show spaces 

0x04 show paragraph end markers 

0x08 show hyphens 

0x10 show forced line breaks 


i ta record {t: 1 2 
This record contains information required to format the document for the printer and to control the 


printing process. It includes, amongst other items, descriptions of the page size and margins, the page 
numbering style, header and footer position and alignment. 


This record consists of a structure that is used to control the display and WDR printing of formatted text 
in all applications that require such services. It therefore contains some fields that are not relevant to the 
word processor. 


The following description is in terms of the P_EXTENT, SCRLAY_FONT and PAGES HEADER structs, defined as: 


typedef struct 
{ 
WORD x; 
WORD y; 
} P_POINT; 


72 


6 WORD PROCESSOR FILE FORMAT 


typedef struct 
€ 
P_POINT tl; 
WORD width; 
WORD height; 
> P_EXTENT; 


typedef struct 
€ 
UWORD fid; 
UWORD style; 
UWORD height; 
}> SCRLAY_FONT; 


typedef struct 
€ 
SCRLAY_FONT f; 
UBYTE align; 
UBYTE first_page; 
} PAGES_HEADER; 


/* typeface number */ (1) 
/* font style */ (2) 
/* height of font in twips */ 


/* font data */ 
/* header alignment */ (3) 
/* TRUE to emit on first page */ 


In these terms, the content of the printer-related record is: 


PAGE DATA 


WORD width 
WORD height 
P_EXTENT body 
WORD hdtop 
WORD hdbot 
WORD pdrflags 
WORD docflags 
UWORD pgbeg 
UWORD pgend 


RUNNING PAGE HEADERS 


PAGES_HEADER top 
PAGES HEADER bot 


PAGE NUMBERING 


WORD offset 
WORD last 
WORD style 


MISCELLANEOUS 


SCRLAY_FONT f 
UBYTE size_choice 
UBYTE wo_control 
UWORD spare’ 
UWORD spare2 


page width 

page height 

body print region, with respect to top left of page (4) 

page header position (5) 

page footer position (6) 

0 for portrait, 1 for landscape 

can be 3 or 0, depending on whether the document has or has not been printed 
page number to start printing (first page is 1) 

last page number to print (7) 


page header 
page footer 


page numbering offset 
page count, for %m (always reset by pagination) 
page number style (8) 


base font for body print region (9) 

paper size index (10) 

TRUE to disable widow and orphan control 
reserved for future use 

reserved for future use 


73 


ADDITIONAL SYSTEM INFORMATION 


Notes 
(1) Typeface numbers are defined in the following list: 


0 COURIER 22 OPTIONAL_SB 44 RUSSIAN 

1 PICA 23 OPTIONAL_SC 45 OPTIONAL_B 
2 ELITE 24 TIMES ROMAN 46 OPTIONAL_C 
3 PRESTIGE 25 CENTURY 47 OPTIONAL_D 
4 LETTER_GOTHIC 26 PALATINO 48 NARRATOR 

5 GOTHIC 27 SOUVENIR 49 EMPHASIS 

6 CUBIC 28 GARAMOND 50 ZAPF_CHANCERY 
7 LINEPRINTER 29 CALEDONIA 51 OPTIONAL_DA 
8 HELVETICA 30 BODONI 52 OLD_ENGLISH 
9 AVANT_GARDE 31 UNIVERSITY 53 OPTIONAL_DB 
10 SPARTAN 32 SCRIPT 54 OPTIONAL_DC 
11 METRO 33 SCRIPT_PS 55 COOPER_BLACK 
12 PRESENTATION 34 OPTIONAL_SCA 56 SYMBOL 

13. APL 35 OPTIONAL_SCB 57 LINE_DRAW 

14 OCR_A 36 COMMERCIAL_SCRIPT 58 MATH_7 

15 OCR_B 37 PARK_AVENUE 59 MATH_8 

16 STANDARD_ROMAN 38 CORONET 60 DINGBATS 

17 EMPEROR 39 OPTIONAL_SCC 61 EAN 

18 MADELEINE 40 GREEK 62 PC_LINE 

19 ZAPF_HUMANIST 41 KANA 63 OPTIONAL_SYA 
20 CLASSIC 42 HEBREW 

21 OPTIONAL_SA 43 OPTIONAL_A 


A typeface that is not supported by the current printer will be mapped to a supported typeface. See the 
later Printer driver font mapping section. 


In addition, a typeface number of -1, indicating an inherited font, is allowed in all emphases and in all 
paragraph styles except Body text. An emphasis inherits its font from the surrounding paragraph; a 
paragraph style inherits its font from the Body text style. 


(2) The font style may be zero, or any sensible combination of the following attributes: 


0x01 underline 
0x02 bold 

0x04 italic 

0x08 superscript 
0x10 subscript 


Styles or style combinations that are not supported by the current printer are ignored. 


The value 0x4000 is used internally by the word processor and may or may not be set in the font style data 
in the document. If set, it can safely be ignored. 


(3) The running page header alignment may be any one of: 


0 left aligned 

1 right aligned 
2 centred 

4 2-column 

5 3-column 


(4) In terms of the body struct and the page width and height, the page margins are: 


left body. tl.x 

top body.tl.y 

right width-body.tl.x-body. width 
bottom height-body.tl.y-body. height 


(5) The page header position measures the vertical distance between the bottom of the header text and the 
top edge of the page body print region. 


(6) The page footer position measures the vertical distance between the bottom of the footer text and the 
bottom edge of the page body print region. 


(7) To print all pages, the last page number should be set to Oxffff. 


74 


6 WORD PROCESSOR FILE FORMAT 


(8) The page number style is one of: 


i) Arabic 
1 Roman, upper case 
2 Roman, lower case. 


(9) The body area base font data is not used by word processor documents. It should always specify the 
default font, with font number 0 (Courier) a style of 0 (normal) and a size of 240 (12 points). 


(10) The page size choice should correspond with the earlier page width and height. The following 
(English) page sizes are recognised: 


Choice Pagetype Width Height 
A4 


0 11906 16838 
1 Custom - - 
2 Executive 10440 15120 
3 Legal 12240 20160 
4 Letter 12240 15840 
5 Monarch 5580 10800 
6 DL 6236 12472 


Note that items 2 to 6 may be different in non-English versions of the software. 


The record consists of a zero terminated string, containing the full path name of the current printer driver 
file, preceded by a one byte index to the printer model within the file. 


The default value is: 


0 
"ROM: :\BJ.WDR" 


The record contains the page header text as a zero terminated string. The string may not exceed 80 bytes. 


eee 


The record contains the page footer text as a zero terminated string. The string may not exceed 80 bytes. 


The file may contain up to 64 such records, each of which defines a single style. 


The following description is in terms of the SCRLAY_FONT struct (described above) and the SCRLAY_MARGINS, 
SCRLAY_SPACING and SCRLAY_TABSTOP structs, defined as: 


typedef struct 
{ 
UWORD Left; /* left margin */ (1) 
UWORD right; /* right margin */ 
UWORD indent; /* left margin for first line of a paragraph */ 
UWORD align; /* alignment */ (2) 


} SCRLAY_MARGINS; 


typedef struct 
{ 


UWORD Line; /* space between lines in a paragraph */ 
UWORD above; /* space above paragraph */ 
UWORD below; /* space below paragraph */ 
UWORD flags; /* keep together/next and new page */ (3) 
} SCRLAY_SPACING; 
typedef struct 
{ 
UWORD x; /* tab position */ 
UWORD type; /* tab type */ (4) 


} SCRLAY_TABSTOP; 


75 


ADDITIONAL SYSTEM INFORMATION 


In these terms, the content of the style data record is: 


TEXT sc([2] two-letter short code (5) 
TEXT tag[16] style tag name (6) 

UWORD sflags style control flags (7) 
SCRLAY_FONT f paragraph base font (8) 
UWORD inherit inherited attributes (9) 


SCRLAY_MARGINS marg margin positions 

SCRLAY_SPACING spe paragraph vertical spacing 

UWORD olevel outliner level 

UWORD ntabs number of tabstops in following table 
SCRLAY_TABSTOP tabI[8] up to 8 tabstops, in ascending position order 


Notes 
(1) Paragraph margins are relative to the page margins (the left and right edges of the body print region). 


(2) The paragraph alignment may be one of: 


i) left aligned 

1 right aligned 

2 centred 

3 justified 

(3) The spacing flags may be any combination of: 

0x01 keep on same page as following paragraph 
0x02 keep whole paragraph on one page 

0x04 paragraph starts a new page 


(4) The tab type may be any one of the following: 


0 left tab 
1 right tab 
2 centred tab 


(5) The short code must contain two alphabetic characters and must be unique (it must not be duplicated 
in either a style or an emphasis). If no meaningful short code can be assigned, it is recommended that 
unique short codes be generated in the sequence ZA,ZB,...,ZZ,YA,...,YZ,XA,... 


(6) Although not currently enforced by the software, the text of the tag name should be unique (it should 
not be duplicated in either a style or an emphasis). If no meaningful tag name can be assigned, it is 
recomended that the tag name should duplicate the short code. 


(7) The style control flags may contain any combination of: 


0x02 undeletable 
0x04 default 


There must be one default style and at least one undeletable style in every document (in all Psion 
documents they are the same - style BT). It does not make sense for the default style to be deletable. 


The value 0x8000 is used internally by the word processor and may or may not be set in the style control 
flags data in the document. If set, it can safely be ignored. 


(8) In addition to the typeface numbers listed earlier, a typeface number of -1 is used to signify that the 
typeface and font size are to be inherited from the default style. In such a case the font size is 
conventionally set to zero. 


(9) The inherited attributes field may contain any combination of: 


0x01 underline 
0x02 bold 
0x04 italic 


For each bit that is set, the corresponding bit in the style field of the ScRLAY_FoNT struct must be clear. 


Inherited attributes are taken from the default style. 


Emphasis data record (type 7, length 28} 


The file may contain up to 16 such records, each of which defines a single emphasis. 


76 


6 WORD PROCESSOR FILE FORMAT 


The following description is in terms of the scRLAY_FONT struct (described above). In these terms, the 
content of the emphasis data record is: 


TEXT sc(2] two-letter short code (1) 
TEXT tag{16] emphasis tag name (2) 
UWORD sflags emphasis control flags (3) 
SCRLAY_FONT f emphasis font (4) 

UWORD inherit inherited attributes (5) 
Notes 


(1) The short code must contain two alphabetic characters and must be unique (it must not be duplicated 
in either a style or an emphasis). If no meaningful short code can be assigned, it is recommended that 
unique short codes be generated in the sequence ZA,ZB,...,ZZ,YA,...,.YZ,XA,... 


(2) Although not currently enforced by the software, the text of the tag name should be unique (it should 
not be duplicated in either a style or an emphasis). If no meaningful tag name can be assigned, it is 
recomended that the tag name should duplicate the short code. 


(3) The emphasis control flags must contain the value 0x01, together with any combination of: 


0x02 undeletable 
0x04 default 


There must be one default emphasis and at least one undeletable emphasis in every document (in all Psion 
documents they are the same - emphasis NN). It does not make sense for the default emphasis to be 
deletable. 


(4) In addition to the typeface numbers listed earlier, a typeface number of -1 is used to signify that the 
typeface and font size are to be inherited from the enclosing paragraph style (which may itself inherit 
from the default style). In such a case the font size is conventionally set to zero. 


(5) The inherited attributes field may contain any sensible combination of: 


0x01 underline 
0x02 bold 

0x04 italic 

0x08 superscript 
0x10 subscript 


For each bit that is set, the corresponding bit in the styte field of the scRLAY_FONT struct must be clear. 


Inherited attributes are taken from the enclosing paragraph style (which may itself inherit from the 
default style). 


terminated by a zero. 


The content conforms with the IBM Code Page 850 symbol set, together with the following additional 
symbols: 


Symbol ASCH Meaning 

END_PARAGRAPH 0x00 paragraph terminator 

HARD_HYPHEN 0x07 unbreakable hyphen, not a word delimiter 
TAB 0x09 tab character 

LINE_FEED Ox0a forced line break 

SOFT_HYPHEN Ox0e ‘optional, or potential, hyphen 

HARD_SPACE Ox0f unbreakable space, not a word delimiter 


nt index record (type 9, variable length} 


This record contains a number of six byte entries which are used to apply style and emphasis to the 
document text. Each entry consists of: 


= a length (word) 


= the two character short code of a style 


77 


ADDITIONAL SYSTEM INFORMATION 


= the two character short code of an emphasis 
The entries conform to the following rules: 


® the sum of their lengths is one more than the length of the document text record (i.e. the 
document size, including the final paragraph terminator that is not contained in the document 
text record). 


= there is at least one index entry for each paragraph in the document 
= the final index entry for each paragraph includes the zero that terminates the paragraph 


= the short codes must correspond with styles and emphases contained in the earlier style data and 
emphasis data records 


It must be stressed that a single index entry may not apply to text that is contained in more than one 
paragraph. 


aS re ee a ee Se a a] 
Template files 


Word processor template files have exactly the same structure as normal word processor files. The only 
differences are that they: 


= have a.wrt extension, rather than .wrd; 
® are stored in a \wdr directory. 
Template files for program editor aliases of the word processor contain only: 
= a40 byte header, as for other word processor files; 
= atype 1 options data record; 
® atype 8 document text record. 
The meanings of some items in the options data record differ from those of a normal word processor file: 
UWORD cpos the saved cursor position, as for normal word processor files 
UBYTE symbols the visibility of screen symbols, as for normal word processor files 


UBYTE backup/statzoom backup state or status window and zoom settings, as for normal word 
processor files 


UBYTE style TRUE to set the use of a bold typeface 

UBYTE mono TRUE to set the use of a monospaced typeface 

UBYTE outlevel always TRUE for program editor aliases, to enable the use of templates 
UBYTE spare TRUE to specify the use of auto indentation 

UWORD spare2 the spacing, in column units, for tabs 


LSS ae ae a 
Printer driver font mapping 


The font ID associated with a particular emphasis or paragraph style is determined by a selection from 
the fonts available from the current printer driver at the time the emphasis or style was created. When a 
different printer driver is installed, the font IDs associated with emphases and styles do not change (so 
that the exact appearance of the document will be restored when the original printer driver is reinstalled). 


The actual font to use when printing the document is determined by mapping each fixed font ID to one of 
the available fonts supplied by the current printer driver. A perfect match occurs when the font ID 
matches the font number of one of the fonts supported by the printer driver. Otherwise, an attempt is 
made to make a simple fixed mapping, by font characteristics, onto a basic set of COURIER (mono), 
HELVETICA (proportional, sans serif) or TIMES ROMAN (proportional, serif). 


78 


6 WORD PROCESSOR FILE FORMAT 


If the current printer driver does not contain fonts with font numbers HELVETICA or TIMES ROMAN, 
thease are also mapped to COURIER (font number zero). This is assumed always to be set for the 
printer's default font - usually a 10 or 12 point font - whatever it is called. 


79 


CHAPTER 7 


SERIES 3/3A AND MC SPREADSHEET FILE FORMAT 


This document describes the structure of the .spr files that are used by the MC, Series 3 and Series 3a 
spreadsheets. The description is to a level that allows other software to read and write non-password- 
protected spreadsheet files. 


i a re ae ee eee a 
File header 
The spreadsheet starts with the following header: 


TEXT fid£16]="SPREADSHEET" packed with trailing zeros 
UWORD vers=0 

UWORD offset=0 

UWORD rtvers=0 


This header must be supplied exactly as described, otherwise all current versions of the Spreadsheet will 
refuse to load the file. The only exception is that a password-protected Series 3a spreadsheet file has a 
file type identifier string (in fid(}) of "SPREADSHEETB". This prevents it from being recognised on Series 3 
and MC machines, where password protection of spreadsheet files is not supported. 


Sa a a es ee ee a ee ee a eee 
Records 
The remainder of the file consists of type/length records, each of which has the following structure: 


UWORD type the record type 
UWORD Len the record length 
record body len bytes of data 


The structure of the data in the record body is dependent on the record type. Currently the following 
types are defined: 


Formula 

Cell contents 

Column width 

Default column width 
Status information 
Display information 
Named range 

Print range 
Database/criteria ranges 
10 Table 

11 Print setup (MC) 

12 Font (MC) 

13 Graph (S3) 

14 Current graph index (S3) 
15 Font palette (S3) 

16 Print data (S3) 

17 Printer model (S3) 

18 Header text (S3) 

19 Footer text (S3) 


WO IA MAR WHR 


81 


ADDITIONAL SYSTEM INFORMATION 


20 Display extras (S3) 
21 3a Display extras (S3a) 
22 Password (S3a) 


As indicated in the above table, some of the above types are specific to the MC version and some to the 
Series 3/3a. 


In general records can appear in any order, but there are currently three exceptions: 


= All formula (type 1) records must appear before the cell (type 2) records that access them, and 
the order in which the formula records appear must be preserved. 


= The 3a Display extras (type 21) record, when present, should immediately precede the Display 
information (type 6) record. Otherwise the 3a Display extras record will be ignored. 


«s The Display extras (type 20) record, when present, should immediately precede the 3a Display 
extras (type 21) record, if one exists. In the absence of a 3a Display extras record, the Display 
extras record should immediately precede the Display information (type 6) record. Otherwise the 
Display extras record will be ignored. 


Some record types may appear many times, and not all record types need be present. Some record types 
are expected to appear only once in the file. If more than one of each record of such a type is present, the 
last record will be read and earlier records of the same type will be ignored. The number of times that a 
particular type of record is expected to occur is indicated in the following descriptions of the record 


types. 


Range references 


Within any record, a range reference specifies the top left and bottom right cells of the range in column 
and row units. These references are inclusive so that, for example, the range reference {0,0}, 1,23} 
describes the range A1:B3. 


A Spreadsheet file may contain any number of Formula records. 


Each formula is stored separately from the cell or cells that use it. This allows memory savings to be 
made by storing only one copy of a commonly used formula. 


The order in which the formula records appear is significant. This determines the index, counting from 
zero, used to reference a formula from a cell record. 


The body of a Formula record is structured as follows: 


UWORD use usage count 
UBYTE Len formula length (253 bytes maximum) 
UBYTE form{Len] the formula 


The formula is stored in reverse Polish notation (RPN). Each operand is preceded by a byte that 
identifies its type. Each function or operator is identified by a single byte that follows the operands upon 
which it acts. 


Brackets are stored in the formula exactly as they were entered, but are ignored when a formula is 
evaluated. They are retained only so that the formula can be reproduced and displayed in exactly the 
same form as it was typed in. 


The RPN structure is broken for the set of functions that act upon argument lists of variable length 
(AVG, COUNT, MAX, MIN, STD, SUM and VAR). For these functions a Start token precedes the 
argument list and an End token follows it. In addition, each operand within the list is preceded by a 
special token. 


The tokens used in formulae are as follows: 


Operators 


0x01 ~— Less than 

0x02 _—_—_ Less than or equal 
0x03 = Greater than 

0x04 Greater than or equal 
0x05 = Not equal 

0x06 = Equal 

0x07. 3=Add 


82 


0x08 
0x09 
Ox0a 
0x0b 
Ox0c 
Ox0d 
Ox0e 
Ox0f 
0x10 
Ox11 


Delimiters 


0x12 
0x13 
0x14 
0x15 


Operands 


0x16 
0x17 
0x18 
0x19 
Oxla 


Functions 


Subtract 
Multiply 
Divide 
Power 

Unary plus 
Unary minus 
Logical NOT 
Logical AND 
Logical OR 


String concatenate 


Open bracket 
Close bracket 
Comma 

End of formula 


7 SERIES 3/3A AND MC SPREADSHEET FILE FORMAT 


Double constant (IEEE floating point number) 


Integer constant (WORD) 


Text string (leading-byte-counted string) 
Cell reference (WORD col, WORD row) 
Range reference (WORD tiCol, WORD tlRow, WORD brCol, WORD brRow) 


Column and/or row references can be either absolute or relative. If the top bit of the WORD is set, the 
reference is relative, otherwise the reference is absolute. Relative references are treated as a signed offset 
(ignoring the top bit) from the cell that uses the formula. Note that cell Al is 0,0. 


In the following list, 'x' refers to a numeric argument, 'str' a string and ‘range’ a range reference. 


Ox1b 
Oxlc 
Oxld 
Oxle 
Oxl1f 
0x20 
0x21 
0x22 
0x23 
0x24 
0x25 
0x26 
0x27 
0x28 
0x29 
Ox2a 
0x2b 
Ox2c 
Ox2d 
Ox2e 
Ox2f 
0x30 
0x31 
0x32 
0x33 
0x34 
0x35 
0x36 
0x37 
0x38 
0x39 
Ox3a 
0x3b 
Ox3c 
0x3d 


Cellpointer(x) 
Char(x) 
Code(str) 
Cols(range) 
Cos(x) 
Datevalue(str) 
Day(x) 
Exp(x) 
Hour(x) 
Int(x) 
Iserr(range) 
Isna(range) 
Isnum(range) 
Isstr(range) 
Len(str) 
Ln(x) 

Log(x) 
Lower(str) 
Minute(x) 
Month(x) 
N(range) 
Proper(str) 
Rows(range) 


83 


ADDITIONAL SYSTEM INFORMATION 


84 


0x3e 
Ox3f 
0x40 
0x41 
0x42 
0x43 
0x44 
0x45 
0x46 
0x47 
0x48 
0x49 
Ox4a 
Ox4b 
Ox4c 
Ox4d 
Ox4e 
Ox4f 
0x50 
0x51 
0x52 
0x53 
0x54 
0x55 
0x56 
0x57 
0x58 
0x59 
Ox5a 
0x5b 
Ox5c 
Ox5d 
OxSe 
Ox5f 
0x60 
0x61 
0x62 
0x63 
0x64 
0x65 
0x66 
0x67 
0x68 
0x69 
Ox6a 
Ox6b 
Ox6c 
Ox6d 
Ox6e 
Ox6f 
0x70 
0x71 
0x72 
0x73 
0x74 
0x75 
0x76 
0x77 
0x78 
0x79 
Ox7a 
0x7b 
Ox7c 
Ox7d 
Ox7e 
Ox7f 
0x80 
0x81 


S(range) 

Second(x) 

Sin(x) 

Sqrt(x) 

Tan(x) 
Timevalue(str) 
Trim(str) 
Upper(str) 
Value(str) 

Year(x) 

Atan2(x,x) 
Cell(x,range) 
Exact(str,str) 
Irr(x,x) 

Left(str,x) 
Mod(x,x) 

Npv(x,x) 

Not used 
Repeat(str,x) 
Right(str,x) 
Round(x,x) 
String(x,x) 
Cterm(x,x) 
Date(x,x) 
Davg(range,x,range) 
Dcount(range,x,range) 
Dmax(range,x,range) 
Dmin(range,x,range) 
Dstd(range,x,range) 
Dsum(range,x,range) 
Dvar(range,x,range) 
Find(str,str,x) 
Fv(x,xX,X) 
Hlookup(x,range,x) 
If(x,x,X) 
Index(range,x,x) 
Mid(str,x,x) 
Pmt(x,x,x) 
Pv(x,x,X) 
Rate(x,x,x) 

Sin(x) 

Term(x,x,X) 
Time(x,x,x) 
VLookup(range,x,x) 
Ddb(x,x,x,x) 
Replace(str,x,x,str) 
Syd(x,x,x,x) 
End of avgQ 

End of chooseQ) 
End of count() 

End of maxQ 

End of minO 
End of stdO 
End of sumQ) 

End of varQ) 

Start of avg() 

Start of choose() 
Start of count(Q) 
Start of maxQ) 

Start of minQ 

Start of stdQ 

Start of sum() 

Start of var() 

Range in avgQ 
Range in choose() 
Range in count() 
Range in max() 
Range in min() 


7 SERIES 3/3A AND MC SPREADSHEET FILE FORMAT 


0x82 Range in stdO 
0x83 ~—s- Range in sumQ) 
0x84 ~=—- Range in var() 
Ox85 = Cell in avg0) 
0x86 ~—- Cell in chooseQ 
0x87 = Cell in count() 
0x88 = Cell in maxQ) 
0x89 = Cell in minQ) 
Ox8a = Cell in std 
Ox8b = Cell in sum() 
Ox8c Cell in varO 


file. 

The body of a Cell record is structured as follows: 

UWORD column Cell co-ordinates (cell Ai is 0,0) 
UWORD row 

UBYTE flags See below 

UBYTE format See below 


cell contents 


Flags 

The ftags byte contains information about the display alignment of the cell and the cell type. 

Xeussene used by the natural order sort and should be left as is 

sXeusces changed flag, TRUE if (and only if) the cell has changed since the last recalc 
ooXeunes numeric alignment, 1 left aligned, 0 right aligned 

oeeXXene text alignment, 00 repeated, 01 left, 10 right, 11 centered 

200 eXXX cell type, defines the format of the contents data as follows: 


000 (0) Blank - contains only the format data shown above and has no contents 
data 


001 (1) Double - contains a floating point constant as a DOUBLE value 
010 (2) Text - contains leading-byte-counted text 
011 (3) Integer - contains an integer (WORD) constant 


101 (5) Double formula - contains a formula that evaluates to a numeric result. 
The contents data is a WORD containing the formula reference, immediately 
followed by a DOUBLE that contains the current resultant value for the cell 


110 (6) Text formula - The cell contains a formula that evaluates to a text 
result. The contents data is a WORD containing the formula reference, 
immediately followed by the leading-byte counted text that represents the 
current resultant value for the cell. 


Each formula reference is an index, counting from zero, determined by the order in which the formula 
records appear in the file. 


85 


ADDITIONAL SYSTEM INFORMATION 


Format 


The format byte records whether or not the cell is currently protected and the way in which numeric 
values should be displayed. 


Xevensce Set if the cell is currently protected 
aXXXXXXX Gives the numeric display format as follows: 


s000XXXx Fixed (xxxx decimal places) 
»001XXXxX Scientific (xxxx decimal places) 
#010Xxxx Currency (Xxxxx decimal places) 
©011XXXX Percentage (xxxx decimal places) 
» 100Xxxx Comma (xxxx decimal places) 
«1110000 Bargraph 

»1110001 General 

*1110010 Date (Lotus DD-MM-YY) 
#1110101 Show formulae 

1110110 Hidden 

©1110111 Time (Lotus HH:MM:SS) 
»1111111 Default 


For the MC Spreadsheet this is the full extent of the cell record. In files generated on the Series 3/3a 
there is an extra trailing byte, following the cell details described above. This extra byte contains a font 
style number, in the range 0-3 inclusive, allowing selection of one of the four fonts that are defined by a 
Font palette (type 15) record. The presence or absence of this additional byte can be deduced by 
calculating the length of the other data in the record and subtracting this value from from the length of 
the record. 


A record of this type is present for each column that is not of the default width. The record body is as 
follows: 


UBYTE column the column number (0 for column A) 
UBYTE width the width of the column (in characters) 


A Spreadsheet file contains only one record of this type. The record body is as follows: 


UWORD width width (in characters) 


This record specifies the width of all columns for which there is no specific Column width (type 3) 
record. 


A Spreadsheet file contains only one record of this type. It stores assorted information that relates to the 
spreadsheet as a whole. The body of a Status information record is structured as follows: 


UWORD flags see below 
UBYTE defForm the default numeric display format 
UBYTE defAlign the default text and numeric alignments 


The numeric display format is one of the values described earlier for a Cell (type 2) record. It is the 
display format used for all cells whose Cell record specifies the default numeric display format. Clearly, 
defForm may not itself be set to the default value. 


The value of defAlign contains the default text and numeric alignments, as described earlier for a Cell 
(type 2) record. Each newly created cell is set to use the alignments specified by defAtign. 


Flags 

The four least significant bits of flags are used for the following purposes: 
eusenesX set if auto recalc is on 

seseoeXe set is protection override is on 

sunenXee set if cells have been deleted since last recalc 
anenXene set if Table recalc is on 


86 


7 SERIES 3/3A AND MC SPREADSHEET FILE FORMAT 


ory one Display information record is ae to appear in the file. The ebaGFOI of a record of this type 
contains the current state of the display for the spreadsheet. It is structured as follows: 


UWORD titleTLCol range for the titles 
UWORD titleTlRow 

UWORD titleBrCol 

UWORD titleBrRow 

UWORD topCol 

UWORD topRow 

UWORD selTICol select range 

UWORD selTLRow 

UWORD selBrCol 

UWORD selBrCol 


UWORD cursorCol position of cursor 

UWORD cursorRow 

UBYTE lines true if grid lines are to be displayed 
UBYTE hideZeros TRUE if zero values are hidden 

Ni ; e 7, length 


Each Named range record specifies a range or cell to be associated with a name. There may be any 
number of such records in a file. The body of the record is structured as follows: 


TEXT name [16] zero terminated text string 
UWORD the range associated with name 
range_left_column 

UWORD range_top_row 

UWORD 

range_right_column reference type 

UWORD range_bottom_row 

UWORD type 


The value of type is 25 (0x19) for a cell reference and 26 (0x1a) for a range reference. These values are 
chosen to match the reference tokens used in formulae. 


A record of this type specifies a range to be offered for selective printing. There may be any number of 
these records in the file. The body of a Print range record is structured as follows: 


UWORD the range 
range_left_column 

UWORD range_top_row 

UWORD 

range_right_column 

UWORD range_bottom_row 


oe 9, length 16) — 


A record of this type ne ihe criterion and database ranges to be used by the database commands. A 
Spreadsheet file is expected to contain zero or one Database/criterion records. The body of the record is 
structured as follows: 


UWORD crit_left_col criterion range 
UWORD crit_top_row 

UWORD crit_right_col 

UWORD crit_bottom_row 

UWORD dbase_left_col database range 
UWORD dbase_top_row 

UWORD dbase_right_col 

UWORD dbase_bottom_row 


formation {type 10, length 16) 


A Spreadsheet file is expexted to contain zero or one Table information records. The body of the record 
is structured as follows: 


87 


ADDITIONAL SYSTEM INFORMATION 


UWORD range_left_col table range 
UWORD range_top_row 

UWORD range_right_col 

UWORD range_bottom_row 


UWORD input1_col input cell i 
UWORD input1_row 
UWORD input2_col input cell 2 


UWORD input2_row 


For table 2 both input cells are valid. For table 1 input2_colt must be set to Oxf fff (65535). 


to appear in the file. The body of the record is structured as follows: 
UWORD type 


Allowed values for type are: 


eensesnnX if TRUE show values, else show formulae 
sescccsXe if TRUE, show hidden cells 

scccasXen if TRUE, show column seperators 
sueeeXeas if TRUE, show headers 


The Font record only appears in an MC Spreadsheet file, and only only one such record is expected to 
appear in the file. The body of the record is structured as follows: 


UWORD type 
TEXT name[16] font name 


The value of type specifies the associated style, as indicated below: 


eanesnnX bold 
euseXaca double height 


88 


7 SERIES 3/3A AND MC SPREADSHEET FILE FORMAT 


A Graph record dtcaly, appears in a Series 3/3a Spreadsheet file, and there may be any number of records 
of this type in the file. Each record defines a displayable graph. The body of the record is structured as 
follows: 


TEXT name [16] zero terminated name for the graph 
UWORD A_range[4] a data range left_col, top_row, right_col, bottom_row 
UWORD B_range[4] 

UWORD C_range[4] 

UWORD D_range[4] 

UWORD E_range([4] 

UWORD F_range[4] 

UWORD X_range[4] 

UWORD A_labels [4] range containing labels for data set A 
UWORD B_labels[4) 
UWORD C_labels [4] 
UWORD D_labels [4] 
UWORD E_labels [4] 
UWORD F_labels [4] 
UBYTE fmts [6] 

UBYTE aligns [6] 

UBYTE xAxisScaling 
UBYTE xAxisFormat 
DOUBLE xAxisLowerLimit 
DOUBLE xAxisUpperLimit 
UBYTE yAxisScaling 
UBYTE yAxisFormat 
DOUBLE yAxisLowerLimit 
DOUBLE yAxisUpperLimit 
UBYTE graphType 

UBYTE gridFlags 

UBYTE colour 

UBYTE rangefF lags 

UBYTE LabelFlags 

UBYTE otherF lags 

WORD skip 

ten strings 


There are ten zero terminated strings packed consecutively, following skip. These strings are, in order: 


First line of title - max length 40 
Second line of title - max len 40 
Title for x axis - max len 40 
Title for y axis - max len 40 
Legend for A range - max len 20 


Legend for F range - max len 20 


A record af this type shine appears in a Series 3/3a a Sorseithioet file, and vail one Current sn avanhite index 
record is expected to appear in the file. The body of the record is structured as follows: 


UWORD curGraph 


It contains the index of the graph that is currently selected, as set by the 'Use graph’ menu item. Graphs 
are indexed in the order that the corresponding Graph (type 13) records appear in the file, with an index 
of 0 referring to the first graph. 


89 


ADDITIONAL SYSTEM INFORMATION 


penewate 


A record of this type only appears in a Series 3/3a Spreadsheet file, and only one Font palette record is 
expected to appear in the file. The body of the record is structured as follows: 


SCRLAY_FONT font1 
SCRLAY_FONT font2 
SCRLAY_FONT font3 
SCRLAY_FONT font4 


Each ScRLAY_FONT structure determines one of the four selectable fonts and their corresponding styles 
(bold, italic etc.). The SCRLAY_FONT struct is defined in scrlay.g as: 


typedef struct 
{ 


UWORD fid; window server font id 

UWORD style; font style (eg bold) 

UWORD height; height of printer font in decipoints 
} SCRLAY_FONT; 


A Print data record only appears in a Series 3/3a Spreadsheet file, and only one record of this type is 
expected to appear in the file. 


This record contains a PRINTER_PARAMS struct that supplies information required to format the document 
for the printer. It includes, amongst other items, descriptions of the page size and margins, the page 
numbering style, header and footer position and alignment. 


The detailed description of this record is outside the scope of this document. For some further details of 
this record, and of the other printer-related records (types 17, 18 and 19), see Saving and restoring print 
context from file in the Printing chapter of the Object Oriented Programming Guide. 


A Printer model record only appears in a Series 3/3a Spreadsheet file, and only one record of this type is 
expected to appear in the file. The body of the record is structured as follows: 


UBYTE model Index 
TEXT ztsDriver 


The data consists of a zero terminated string, containing the full path name of the current printer driver 
file, preceded by a one byte index to the printer model within the file. The default value is: 


0 
"ROM: :Bd.WDR" 


This record type only appears in a Series 3/3a Spreadsheet file, and only one Header text record is 
expected to appear in the file. The body of the record is structured as follows: 


TEXT zts 


The zero terminated string contains the text used for the page header when printing. 


Foote 


This record type only appears in a Series 3/3a Spreadsheet file, and only one Footer text record is 
expected to appear in the file. The body of the record is structured as follows: 


TEXT zts 


The zero terminated string contains the text used for the page footer when printing. 


90 


7 SERIES 3/3A AND MC SPREADSHEET FILE FORMAT 


A Display extras record only appears in a Series 3/3a Spreadsheet file. A record of this type is ignored 
unless it immediately precedes either the 3a Display extras (type 21) record, if it exists, or the Display 
information (type 6) record. The body of the record is structured as follows: 


UWORD flags 
Only two bits of flags are used: 


sescenoX set if grid labels are shown 
euseeaXe set if small font is used (this is over-ridden if a 3a Display extras (type 21) 
record is present. 


A 3a Display extras record only appears in a Series 3a Spreadsheet file. A record of this type is ignored 
unless it immediately precedes the Display information (type 6) record. The body of the record is 
structured as follows: 


UBYTE font font used 
UBYTE statwin status window 
WORD spare for future expansion 
Font 
This takes one of the values: 
255 smallest font available 
8 Swiss 8 pixel font 
9 Swiss 11 pixel font 
10 Swiss 13 pixel font 
11 Swiss 16 pixel font 
Statwin 
This takes one of the values: 
0 No status window 
1 Small status window 
2 Large status window 


SBS AAS ESA ASAE RSNA BERANE ANI st NaS tomate ae NS ames SSL anette 


This record is present only if the spreadsheet is password-protected. Its content is used as part of the 
spreadsheet encryption/decryption process. 


91 


CHAPTER 8 


WRITING DEVICE DRIVERS 


waa SS re ee ee ee Se ee 
Introduction 


This chapter is aimed at the programmer who wishes to write an installable device driver and anyone who 
wishes to improve their general device driver background. The details of communicating with device 
drivers can be found in the J/O System chapter of the PLIB Reference manual. Details of the resident 
device drivers can be found in the appropriate chapters of the J/O Devices manual and the PLIB Reference 
manual. The Borland Turbo assembler was used throughout. See also the following Example Device 
Drivers chapter. 


Psion SIBO machines are supplied with a set of resident device drivers built in to the ROM each of which 
can be replaced with an installable device driver having the same name. Installable device drivers can 
also be added to increase the number of available device drivers. Installing a device driver is carried out 
dynamically without resetting the machine (this is not the case with many operating systems). 


The device driver performs the logical processing required to translate low level hardware instructions 
into high level services suitable for an application. Conventionally, device drivers are divided into a 
logical layer riding astride a physical layer. The physical device driver (PDD) contains the code required 
for talking directly with the hardware device and provides a set of low level hardware specific services. 
The logical device driver (LDD) performs the logical processing that transforms these low level services 
into the high level services used by an application. 


The following example illustrates the two layer nature of device drivers. An application using the serial 
driver decides that it requires RTS/CTS handshaking. It calls an LDD which decides whether or not a 
line should be driven. If the answer is yes the LDD calls the appropriate PDD and asks for a specific line 
to be driven to a specific state. The PDD duly carries out the requested service. 


In the above example the LDD could have talked directly with the hardware. However Psion SIBO 
machines will often use the same LDD with a PDD written specifically for each version of the hardware 
device. Splitting the device driver is thus highly desirable. 


An LDD must provide eight functions for use by the operating system. The functions are passed to the 
operating system via a table of function offsets (sometimes called the vector function table). These 
functions are mandatory. Similarly a PDD must provide two functions for use by the operating system 
and may provide a further two if required. 


An LDD will usually provide further services/functions for use by an application. The form that these 
take is dependent on the LDD requirements and the functions supplied by the associated PDD(s). It is 
advisable to adopt the predefined system defines for these services as this allows the LDD to receive I/O 
requests via the usual route (p_read, p_write etc). 


A PDD will usually define further services specifically for use by LDDs or (less frequently) 
applications. ~ 


The operating system will send device drivers system events not sent to other applications. Examples are 
events generated by the machine being switched on or off, memory segments being moved about and the 
owning application being panicked. 


Any device driver configuration that has associated hardware interrupts must contain at least an LDD. 


The EPOC operating system can handle a maximum of 32 device drivers on a Series3 machine and 48 on 
other machines. 


93 


ADDITIONAL SYSTEM INFORMATION 


The location of device drivers 
Resident device drivers are built into the operating system, with the code residing in the ROM. 


Installable device drivers are loaded into a device memory segment from the file in which they exist. The 
device memory segments are created, owned and managed by the SYS$FSRV process. 


Under no circumstances should any application or device driver attempt to create, delete or change the 
size of a device memory segment. 
Device Driver Names 
Device drivers are known to the EPOC operating system by their names. 
A logical device driver name always has three characters followed by a colon. For example: 
= TTY: is the serial LDD 
8 TIM: is the timer LDD 
m@ SND: is the sound LDD 


A physical device driver name always has three characters followed by a period, a further three 
characters and a colon. For example: 


@  =TTY.UAR: is the 16450 UART driver 
m  6TTY.AS5: is the ASICS driver 


The first three characters of a PDD name are the name of the LDD to which the PDD belongs. The 
second set of three characters uniquely identify the PDD. In the above examples both PDDs belong to the 
TTY: LDD. 


The name of the device driver is the mechanism by which an application can obtain a ‘channel’ to the 
device driver. 
Device Driver Channels 


To obtain a channel to an LDD, an application should call the 1o0pen operating system service. A channel 
can be opened by calling the PLIB library function p_open. For example: 


=  p_open(&chan,"SND:", -1) 
® p_open(&chan,"TIM:",-1) 


To obtain a channel to a PDD, an application should call the DevOpenPop operating system service. 
Typically only LDDs open PDDs. The p_open library function can be used to open a PDD indirectly as 
described below. 


For a device driver configuration consisting of an LDD and a PDD the application will usually open a 
channel to the LDD only: the LDD as part of its initialisation would open a channel to the required 
PDD. A channel can be opened by calling the PLIB library function p_open. For example: 


=» p_open(&chan,"TTY.UAR=",-1) 
*  p_open(&chan,"TTY.AS5:",-1) 


If the LDD requires a PDD and none is specified, it is up to the LDD to either fail the open request or 
hunt for a loaded PDD that it can use. The Try: device hunts for an appropriate PDD. 


A device driver may be capable of supporting more than one open channel at a time. In order to 
distinguish the channels, a qualifier can be added to the open request as part of the device name. It is up 
to the device driver to specify the format of the qualifier. By convention channels are allocated a single 
character sequentially from the character 'A'. For example, the parallel driver can support two open 
channels, 'A' and 'B'. The LDD requires one of these qualifiers in order to open a parallel driver 
channel. 


®  p_open(&chan,""PAR:A",-1) 
*  p_open(&chan, "PAR:B", -1) 


LDDs have been designed to be accessed via the I/O system. I/O requests on the opened channel will 
reach the ‘strategy vector’ of the device driver. 


94 


8 WRITING DEVICE DRIVERS 


PDDs have been designed to be accessed by an LDD either via far calls or the Devvector operating system 
function. 


Searching for PDDs 


In the case that an LDD requires a PDD to provide some hardware specific functionality and the open 
LDD request does not specify a PDD, the LDD should search for a PDD to use. In this case either there 
is only one PDD for that machine (but is different across machines) or the PDDs are capable of 
determining whether it can drive the specified channel. 


For example the serial LDD requires a PDD. However there is currently only one serial PDD on each 
machine (each machine has a different PDD though). By searching for the appropriate serial PDD, the 
serial LDD can be the same on all machines. 


On the other hand there are several filing system PDDs each of which is capable of reading an ID byte 
from a SIBO pack. The PDD can then determine if it is the correct PDD for that hardware or not. 


To search for a PDD, an LDD should use the DevFind service. For example the Fsy: LDD would search 
for all Fsy.* PDDs. As each PDD is found, it can be requested to open the appropriate channel by using 
the DevOpenPbD operating system service. 


Device Driver Hierarchies And Attached Drivers 
LDDs are classified as either root or attached drivers. 


Device drivers exist in a hierarchy the first of which is termed the root driver. The other drivers in the 
hierarchy are termed attached drivers. In the language of object oriented programming, an attached driver 
subclasses the root driver. In this document, the driver to which another driver is attached is referred to 
as the underlying driver. 


An attached device driver requires the underlying driver to provide a specified set of functions. How 
these are implemented is of no concern to an attached driver. For example, the Xmodem device driver is 
an attached driver which can, for example, attach to the serial driver which happens to be a root device 
driver. The Xmodem driver requires the underlying driver to support the serial sense, serial set, read, 
write and close functions. The power of attached drivers comes from the fact that the Xmodem driver 
does not need to know anything about the physical transmission medium, and can run on either serial 
port quite happily. In fact the Xmodem driver could run over any physical medium e.g. telephone, 
parallel, radio, infra-red etc as long as the underlying driver supported the small set of functions 
required. 


Additional power comes from the fact that a driver does not have to be attached to the root driver 
directly: other attached drivers may exist in the hierarchy. For example an Xmodem driver can be 
attached to a modem driver that provides modem configuration and dialling functions. This can in turn be 
attached to the resident serial driver. 


Notice that the Xmodem driver neither knows nor cares about the driver hierarchy. 
There is no limit to the number of drivers that exist in a device driver hierarchy. 


When the operating system routes an I/O request to a device driver, it follows this hierarchy and calls the 
strategy vector of the device driver at the top of the hierarchy. The device driver at the top of the 
hierarchy is the last opened device driver on that I/O channel. 


The routing mechanism is best explained by an example of an application that wishes to use the parallel 
driver with a timeout facility. In its raw form, the parallel driver does not allow for timeouts. Although 
the application could handle this, a neater solution (in terms of application code) is to use an attached 
driver. The application opens the parallel driver in the normal way and then opens the attached driver 
(written as part of the application) passing the currently opened parallel device channel handle to the open 
vector. The attached driver will open a timer channel for itself and use the 1oFuncAttach I/O service on 
the passed opened channel. This request will go to the parallel driver since it is the next down the 
hierarchy. The parallel driver, not supporting this function, passes it on to the operating system (using 
the IoRoot service) to perform the attach service. The parallel drivers open channel handle is returned by 
the attached driver to the caller of the 100pen service. From this point on all I/O requests made on the 
parallel drivers I/O channel handle will be directed to the strategy vector of the attached driver first 
which can then process it and pass on any requests it feels necessary. For example, the loFuncWrite 
request would go to the attached drivers strategy vector. It would queue a timer for an appropriate length 
of time and then pass on the loFuncWrite request to the parallel driver. If the timer expired before the 
write completed, the attached driver would cancel the outstanding write request on the parallel driver and 
inform the application that the timer expired. 


a5 


ADDITIONAL SYSTEM INFORMATION 


In the above example the same attached driver could in fact attach itself to any device driver that requires 
a timeout on the 1oFuneWrite request. 


An attached driver is written in exactly the same way as any other driver. However, if the driver does not 
support a requested function, the strategy vector of an attached driver calls the toSuper operating system 
service rather than the 1oRoot system service. 


R———E—E—EE EE ES ee 
Interrupts and Interrupt Service Routines 


Device drivers that talk to hardware tend to have interrupt service routines associated with them, 
especially if they are receiving data from an external source. 


The EPOC operating system provides a framework within which an interrupt service routine can be 
written relatively easily. 


The SIBO architecture allows for eight independent hardware interrupt sources, some of which are pre- 
allocated to system components (see the ASIC1 section of the Hardware Reference manual for details). 


The operating system provides the GenSetRevector service to allow a device driver to install an interrupt 
service routine for any of the eight hardware interrupt sources. 


A device driver should use this system service and not poke directly into the 8086 interrupt vector table. 
The address passed to the GensetRevector service is not written into the interrupt vector table but to an 
internal table. 


When an interrupt occurs, the operating system builds the mandatory operating system call frame, 
preserving all registers on route. The interrupt service routine is then called as a FAR routine. Since the 
operating system preserves all registers the interrupt service routine is free to use any register. 


To remove the interrupt service routine address, the operating system service GenResetRevector should be 
used. This will reset the internal table entry to the default held in the ROM. 


As with all interrupt service routines various rules apply: 


= Interrupt service routines should execute as fast as possible. Operating system interrupt service 
routines are tuned to last no longer than one millisecond. 


« Typically, interrupt service routines do not enable interrupts unless the routine can handle 
Teentrancy. 


= Interrupt service routines run in the context of whatever process is running at the time of the 
interrupt. An interrupt service routine should not attempt to obtain admissibility to the process 
that opened the channel but access the internal driver space only which in general is its own code 
space. 


= An interrupt service routine must not directly cause a reschedule as this would significantly 
delay its completion. It must use the 1oSignalByPidNoReSched system service in order to indicate 
that an event has occurred to the owning process. The handler function of the device driver must 
pick up the event and inform the owning process. 


= An interrupt service routine should return with the carry flag clear if it requires a reschedule to 
occur (it has called 10SignalByPidNoReSched) otherwise return with the carry flag set. This will 
cause the operating system to reschedule if the internal state allows such an action otherwise the 
reschedule request is effectively queued until such time that the operating system can reschedule. 


Device Driver 1/O Semaphore Waithandlers 


An LDD may nominate one of its functions to be called by the operating system every time the I/O 
semaphore of the process that opened the channel is signalled. The nominated function will only be called 
if the application is waiting for an outstanding I/O request to complete. For well written applications this 
is practically all the time. 


By convention the vector table entry after the mandatory vectors contains the handler vector. 
A handler routine is similar to an interrupt service routine in that it appears to run ‘from nowhere’. 


Comparing handlers and interrupt services routines shows that: 


96 


8 WRITING DEVICE DRIVERS 


« A handler will always run in the context of the process that has opened a channel. An interrupt 
service routine will run in the context of whatever process happens to be running at the time of 
the interrupt. 


=» A handler can access the data space of the process that opened the channel. The interrupt service 
routine must not. An interrupt service routine should only access the data space in the driver 
which is usually its own CS space. 


=s A handler can cause a reschedule. An interrupt service routine must not cause a reschedule. If it 
did, the interrupt would not be fully serviced (the rest of the interrupt service routine would not 
be executed until a reschedule back to the process running at the time of the interrupt, which 
may not happen for a significant length of time). The interrupt service routine must only use the 
loSignalByPidNoReSched to signal the channel owner. 


The handler is the mechanism by which hardware interrupt events can be filtered through to the process 
using the I/O channel. 


Eur ee a a ee eee 
Loadable Logical Device Driver Structure 
A loadable LDD must obey the following rules: 

= There must be a single code segment and no data segments. 

s The code segment must start with a Lib€nt structure. 


= There must be at least eight supported functions. 


Single Code Segment 


An LDD must be written to contain any internal variables within its own code segment. In general these 
variables are only concerned with unit allocation and the hardware state. 


Data space for a particular open channel can be allocated in the heap space of the process that opens the 
device. This data space will however disappear if the process terminates. Therefore any variables 
pce aie for 'freeing' the hardware after a process terminates must exist in the code space of the device 
The LibEnt Structure 
A Libént structure has the following format: 

= two byte signature 

= eight byte name 

=" two byte vector count 

= A vector table 
The two byte signature should contain the 'LopSignature’ define. 


The eight byte name contains the name of the device driver stored as a zero terminated string. Note that 
the trailing colon is omitted. 


The two byte vector count contains the number of vectors that follow immediately after the count. This 
should be equal to at least eight. 


For example: 


97 


ADDITIONAL SYSTEM INFORMATION 


dw LODSignature 3; Its an LOD 

db 'DVR',0,0,0,0,0 ; Name of the driver 

dw (VectorEnd-Vector)/2 ; Number of vectors 
Vector: 

dw Dvrinstall 3; Install vector 

dw DvrRemove ; Remove vector 

dw DvrHold ; Hold vector 

dw DvrResume ; Resume vector 

dw DvrReset ; Reset Vector 

dw DvrUnits ; Units Vector 

dw DvrOpen ; Open Vector 

dw DvrStrategy ; Strategy vector 
VectorEnd: 


The vector table contains the offsets within the device drivers code segment for the functions required by 
the EPOC operating system. Throughout this document the terms vector and function are used 
interchangeably. The vector table must have the entries in the order shown in the example. 
Mandatory LDD Functions 
All LDDs must support the following eight functions: 

@ DevFuncInstall called on device installation. 

™ DevFuncRemove called on device removal. 

= DevFuncHold called to temporarily disable the driver. 

®@ DevFuncResume called to enable the driver after it has been temporarily disabled. 

= DevFuncReset called when an application terminates without closing the channel. 

® DevFuncUnits called to query the number of supported units (i.e. channels). 

@ DevFuncOpen called to open a channel to an LDD. 

= DevFuncStrategy called to access the device drivers functionality from the I/O system. 


All of the routines pointed at by the function vector table will be called FAR by the operating system and 
should consequently use a FAR return machine code instruction to return back to the operating system. 


Since the FAR retum address is to the operating system it does not matter if the operating system moves 
memory whilst code in the LDD is being executed: the operating system cannot move its own code. 


This function is called by the operating system when the device driver is loaded in order to initialise any 
internal variables. It can not be called directly by an application process. 


The Devinstall operating system service will cause this function to be called. Applications should not call 
this service directly and should call instead the DevLoadLDD service. 


An installable device driver may have the same name as a resident device driver. When the operating 
system loads a device driver, it places it at the end of the device driver table. The operating system will 
search this table for the appropriate device driver when it wishes to establish a channel. The search starts 
at the end and thus will locate the most recently installed device driver (if any) or if not, the resident 
driver. By this mechanism an installable driver can replace any resident driver. 


When called, the DS and ES segment registers are in an unknown state. The device driver should take 
whatever steps necessary to obtain direct addressability to its data. For loadable device drivers this 
involves setting the DS and ES registers to the CS register. 


The operating system will not move memory whilst in this function, thus the normal mules governing DS 
and ES may be ignored. 


All operating system services may be called, except those concerning file or device access. 


PASSED 


No values are passed to the install vector. 


98 


8 WRITING DEVICE DRIVERS 


RETURN 
If the installation was successful, return with the carry flag clear. 


If the installation failed, return with the carry flag set and the error number in the AL register. 


PANIC 
The install vector must not panic: it will cause an operating system kernel fault if it does. 


PRESERVE 
The SS, SP and BP registers must be preserved by the install function. 
BD chemo 0 


This function will be called by the operating system when the device driver is requested to be unloaded. 
It can not be called directly by an application process. 


The DevRemove operating system service will cause this function to be called. Applications should not call 
this directly, they should use the Dewelete service. 


Before the remove function is requested, the device driver will have received a hold request. Thus 
devices will only ever be removed when in a heid state. 


If the device driver is currently busy serving a client, the remove request should return an error. 


All resident device drivers will return an error since there is no mechanism by which they can be re- 
installed. 


When called, the DS and ES segment registers are in an unknown state; the device driver should take 
whatever steps necessary to obtain direct addressability to its data. For loadable device drivers this 
involves setting the DS and ES registers to the CS register. 


The operating system will not move memory whilst in this function, thus the normal rules governing DS 
and ES may be ignored. 


All operating system services may be called, except those concerning file or device access. 


PASSED 


No values are passed to the remove vector. 


RETURN 
If the remove was successful, return with the carry flag clear. 


If the remove failed, return with the carry flag set and the error number in the AL register. 


PANIC 


The remove vector must not panic: it will cause an operating system kernel fault if it does. 


PRESERVE 


The SS, SP and BP registers must be preserved by the remove vector. 


hold vector is called in the context of the operating system. 


The DevHold operating system service will cause this vector to be called. Applications should not call this 
service. 


The operating system will call the hold vector under three conditions: 
= Device memory segments are about to be moved. 


= The machine is about to switch off due to the auto switch off timeout or user request, it enters 
the standby state. 


99 


ADDITIONAL SYSTEM INFORMATION 


s The machine is about to switch off due to the power source being removed. 


In all cases the device driver must respond to the request as quickly as possible. It must also ensure that 
ALL interrupts from the hardware device that it is driving are disabled. 


Device memory segments can only be moved if an installable device driver is being installed or removed. 
If the LDD uses an attached PDD and uses the faster FAR call mechanism to call the PDD strategy 
vector, the PDD strategy vector address will potentially move, thus the FAR address will be wrong. This 
address can be resolved in the resume vector. The LDD must not call the PDD between a hold and 
resume. Typically, the device driver only needs to disable its interrupts. When a resume occurs, the 
device driver should continue as though nothing had happened. 


If the machine is about to switch off due to the auto switch off or user request mechanisms (enter the 
standby state), the device driver should make an orderly shut down of the device such that the state 
before the shut down can be recovered when the system powers up again. The device driver should also 
attempt to ensure that no data is lost. For example, in the serial driver the current state of the hardware 
handshaking lines should be noted so that each state can be restored on power up. For this type of power 
down the hold vector is allowed to take a significant length of time to shut down a device. For example 
in a serial driver the hold vector should wait until the remote end stops transmitting data after any 
hardware handshaking has been applied. Of course, the time taken should be kept to a minimum: in the 
case of the serial driver above the time is roughly equivalent to 3 character transmission times. When a 
resume occurs the device driver should continue as though nothing had happened. 


If the machine is about to switch off due to the power source being removed, the device driver should 
reset the device in the minimum possible time: no attempt should be made to perform an orderly shut- 
down. The device driver is not expected to be able to recover the hardware state. When a resume occurs, 
the device driver would typically fail any outstanding application requests. If the hold vector takes too 
long the voltage will fall below the threshold to hold the state of the internal RAM. If this occurs the 
machine will perform a warm re-boot when powering up, all data in the internal memory of the machine 
will be lost including the device driver code! On power fail there is about 2ms available to power down 
all devices. 


On a power failure hold, the operating system will already have sent a 'reset' to all the SIBO serial 
channels. Any device drivers using these channels need only record the hold reason for the resume 
vector. Any other peripherals should be designed to allow a power fail mechanism with the minimum 
amount of code. 


It must be noted that the power fail type hold can occur whilst the device driver is in the memory move 
hold state. In this case, the device driver will receive two hold requests before seeing a resume request. A 
device driver must be capable of handling this. In this case, the device driver will also receive two 
resume requests. A device driver will not get a power fail hold whilst in power down hold. 


A call to the hold vector will always be followed by a call to the resume vector (except when a device is 
requested to be removed). 


When called, the DS and ES segment registers are in an unknown state; the device driver should take 
whatever steps necessary to obtain direct addressability to its data. For loadable device drivers this 
involves setting the DS and ES registers to the CS register. 


The device driver should not call any operating system services due to the time taken, especially on 
power failure. 


PASSED 
The AH register takes one of the following 
® DevHoltdNormal Device memory is about to be moved. 
= = DevHoldPowerDown The system is about to enter the standby state. 
= ~DevHoldPowerFail The system has lost its power supply. 
RETURN 
None. 
PANIC 


The hold vector must not panic: it will cause an operating system kernel fault if it does. 


100 


8 WRITING DEVICE DRIVERS 


PRESERVE 
The SS, SP and BP registers must be preserved by the hold vector. 


resume vector is called in the context of the operating system. 


The DevResume operating system service will cause this vector to be called. Applications should not call 
this service. 


The resume vector will be called either when memory has finished being moved or when the machine 
powers back up. In both cases the hold vector will have been called before this vector is called. 


The device driver is expected to recover from the previous hold request (except power fail) and resume 
any I/O that was suspended. 


If the device driver has an interrupt service routine, it should reset the interrupt service routine's address 
since the device driver may have moved in memory; its absolute segment address will be different. 


If the hold was a device memory segment move type hold, interrupts should be re-enabled. If the LDD 
uses an attached PDD and uses the FAR call mechanism to access the PDD strategy vector, the address of 
the PDD should be reset by using the DevGetPopAddress operating system service before enabling 
interrupts. Typically, the PDD will have a call back to the LDD and it needs to be informed of the 
change of address of the LDD call back function; the LDD-PDD interface definition should allow such a 
function request. 


If the hold was a power down type hold, the resume vector needs to power up the peripheral and set it to 
the state that it was in before the power down occurred. If this is not possible or data has been lost, the 
device driver should inform any outstanding requests of this fact. 


It is also possible that the hardware device that the driver is associated with has been removed. The 
driver should be able to handle this properly. 


If the device driver is expected to generate events due to an external state change, the driver should check 
the external state and generate appropriate events. For example, the serial driver may be requested to 
inform an application when the DTR line changes state. The remote end may have changed the state of 
DTR whilst the driver is held. 


If the hold was a power failure type hold, the resume vector should power up the peripheral and put it 
into a known state, preferably the state that the application software thinks that the device is in and fail 
any outstanding requests as data is quite likely to have been lost. 


When called, the DS and ES segment registers are in an unknown state. The device driver should take 
whatever steps necessary to obtain direct addressability to its data. For loadable device drivers this 
involves setting the DS and ES registers to the CS register. 


All operating system services may be called, except those concerning file or device access. 


PASSED 
None 


RETURN 


None. 


PANIC 


The resume vector must not panic, it will cause an operating system kernel fault if it does. 


PRESERVE 
The SS, SP and BP registers must be preserved by the resume vector. 


“uncReset 2 se. 
This function will be called by the operating system when the device driver is requested to reset a 
channel. The reset function is called in the context of the operating system. 


101 


ADDITIONAL SYSTEM INFORMATION 


The device driver must request that the operating system call the reset function. This is achieved by 
calling the 1oRequestReset system service, usually in the open vector. To cancel this request, the device 
driver should cali the 1oRequestResetCancel system service. The cancel service is usually called as part of 
the close functionality in the strategy vector. 


The reset vector will be called when the operating system is tidying up resources owned by a process that 
has terminated. If a process terminated before it closed the device driver channel and no reset service is 
requested, that channel would remain allocated; no process will ever close the channel. The reset vector 
allows a device driver to reset itself and allow the channel to be opened again. 


Any data required to perform the reset must be stored in the device driver. The data space belonging to 
the process that originally opened the channel has been returned to the operating system memory pool 
and is no longer valid. 


If a device driver can handle multiple channels then the data passed to the 1oRequestReset system service 
should identify the channel. This data will be passed in the CX register to the reset vector. 


The device driver should only have a reset request outstanding with the operating system while a process 
has a channel open. 


When called, the DS and ES segment registers are in an unknown state; the device driver should take 
whatever steps necessary to obtain direct addressability to its data. For loadable device drivers this 
involves setting the DS and ES registers to the CS register. 


All operating system services may be called, except those concerning file or device access. 


PASSED 


This function is passed data in the CX register that the device driver requested it be sent to determine 
which channel should be reset. 


RETURN 


None. 


PANIC 


The reset vector must not panic; it will cause an operating system kernel fault if it does. 


PRESERVE 
The SS, SP and BP registers must be preserved by the reset vector. 


This function will be called by the operating system when the device driver is requested to report the 
number of units (i.e. channels) the device driver can support. This function is called in the context of the 
operating system. 


The operating system places no significance on the number of channels a device driver can support. It is 
primarily used for informational purposes. 


An application may use the number of units to attempt to open any available channel on that device 
driver. 


When called, the DS and ES segment registers are in an unknown state; the device driver should take 
whatever steps necessary to obtain direct addressability to its data. For loadable device drivers this 
involves setting the DS and ES registers to the CS register. 


All operating system services may be called, except those concerning file or device access. 


PASSED 
None. 


RETURN 


The AX register should contain the number of channels supported. If a device driver can support multiple 
channels (limited only by memory constraints) then the driver may return -1. A serial device driver, for 
example, might only support two channels (TTY:A and TTY:8) whereas the file device driver can open an 
unlimited number of files. 


102 


8 WRITING DEVICE DRIVERS 


PANIC 


The channels units vector must not panic; it will cause an operating system kernel fault if it does. 


PRESERVE 
The SS, SP and BP registers must be preserved by the units vector. 


This function w system when a channel to the device driver is required to be 
opened. This function is called in the context of the process that called the 100pen system service. 


The device driver is passed two parameters, its device handle and a pointer to an OpenEnt structure. 


The device handle is the entry in the system device table of this device driver. The device driver is 
required to place this handle in the chant ibHandle field of the chanent structure which must be allocated in 
the user's data space. The operating system uses the device handle to route any I/O requests on the 
opened channel to the correct device driver. 


The OpenEnt structure contains three fields, openNamePtr, OpenMode and OpenChan. 


The OpenNamePtr field contains a pointer to the character that exists after the device name as passed to the 
loOpen system service. For example, if the to0pen service was passed a name of PaR:A, the OpenNamePtr 
field would point to the colon. If the 1odpen service was passed a name of TTY.AS5:B the OpenNamePtr field 
would point to the full stop. The device driver should process the name appropriately, opening the 
correct PDD as required. 


The openMode field contains the mode for opening the device driver. The available modes are specified by 
the device driver writers. For example, a combined Xmodem and Ymodem device driver could use the 
mode to specify whether the Xmodem or the Ymodem protocol is to be used. 


The Openchan field contains the I/O channel handle of the device that this driver is required to ‘attach’ to. 
Attached device drivers are dealt with later in the chapter. 


The code in a device driver open vector tends to follow a very similar pattern. This is demonstrated by 
the following code fragments and associated comments. 


The first stage is to allocate some data space in the calling process’ heap space. This will contain the I/O 
channel control block: 


mov cx, (size DeviceEnt) 

HeapAl LocateCelt 

jc noMemory 

mov bx, ax ; cell handle 


If the device driver requires a WaitHandler (described later): 


mov al, (VectorHandler-Vector)/2 
ToAddkKandler 

jc endFreeMemory 

mov [bx] .OriverHandler, ax 


If the device driver's DevFuncReset vector is required to be called: 


push bx 

mov cx, ChannelIndicator 3 unique per channel 
mov bx, dx ; the device handle 
ToRequestReset 

pop bx 3 restore alloc cell 


The chanent field of the priverEnt structure must be initialised: 


mov [bx] .Driverlo.ChanNext, bx 
mov (bx] .Driverlo.ChanSignature, IoChanSignature 
mov (bx] .Driverlo.ChanLibHandle, dx 


The chanNext field is used by attached drivers and will usually be set to be the allocated cell handle of the 
device driver being opened. The loFuncAttach and IoFuncDetach functions manipulate these fields. The 
I/O system uses this field to direct the I/O request to the correct driver. 


103 


ADDITIONAL SYSTEM INFORMATION 


The ChanSignature field is checked by the operating system during any I/O requests for the value 
loChanSignature. If it does not contain that value, the process calling the I/O service will be panicked for 
having passed an invalid I/O channel handle. 


The ChanlibHandle field is used by the operating system to route an application's I/O request to this 
device. The I/O request will call the DevFuncStrategy vector of the device driver. 


- If the driver is an attached driver the following is required: 


allocated channel 
channel attaching to 
return in BX the 
channel attached to 


mov cx, bx 

mov bx, [si] .OpenChan 
mov al, IoFuncAttach 
ToWithWait 


ma me me Be 


Finally, if the channel has been successfully opened: 


cic ; Opened Ok 
ret ¢ return BX and DX 


The error recover code typically follows the following pattern: 


endFreeReset: 
push ax 
push bx 
mov cx, Channel Indicator 
mov bx, dx 
loRequestResetCancel 
pop bx 
pop ax 
endFreeHandler: 
push ax 
push bx 
mov bx, [bx] .DriverHandler 
loRemoveHandler 
pop bx 
pop ax 
endFreeMemory: 
push ax 
HeapFreeCel L 
pop ax 
ste 
noMemory: 
ret 


If a device driver supports a fixed number of channels, it typically contains static control blocks. In order 
to determine if a requested channel is currently open, a field should be interrogated. The device driver 
should ensure that interrupts are disabled during this sort of check since a context switch could occur and 
another process request the opening of the same channel. This is the classic ‘test and set' problem 
encountered in multi-tasking environments. 


When called, the DS and ES segment registers point to the data segment of the application process 
attempting to open a device channel. The application should ensure that the DS and ES segment registers 
do in fact point to its data segment. The device driver must obey the normal rules concerning segment 
register manipulation. The DS and ES segment registers can be reloaded if required from the tntEnt 
structure pointed at by the BP register. 


All operating system services may be called. 


PASSED 
DX contains the device handle of the device driver. 
SI is a pointer to the OpenEnt structure 


BP is a pointer to the IntEnt structure. 


RETURN 


If the channel open was successful, return with the carry flag clear and the BX register containing the 
open channel. 


If the open failed, return with the carry flag set and the error number in the AL register. 


104 


8 WRITING DEVICE DRIVERS 


PANIC 


The open vector can panic; it will cause the process requesting the device open to terminate. It is 
however more usual to return an error to the calling process. 


PRESERVE 
The DS, ES, SS, SP, BP and DX registers must be preserved by the open vector. 


When an application makes an I/O request on the opened device driver channel the request is routed to 
this vector by the operating system. A device driver defines the set of functions that it supports. These 
typically include 1oFuncSet, loFuncSense, IoFuncRead, IofuncWrite and IoFuncClose. A device driver does 
not have to support any particular function, as it is a matter of design between a device driver writer and 
application writer as to what functions and associated parameters are provided. 


To obtain the power of attached device drivers, it is recommended that the device driver use the system 
defines with their appropriate functionality, for example, the 1oFuncWwrite function number should always 
be associated with writing data. 


The strategy function is passed the channel handle as allocated in the open vector in the BX register. This 
typically contains control information concerning the current state of the I/O channel. 


The SI register contains a pointer to a RqEnt structure. This structure contains four fields, RqFunction, 
RqStatusPtr, RqAiPtr and RqA2Ptr. 


The RqFunction field contains the function number as passed to the 1oWithWait (or loAsynchronous) I/O 
request by the application. If a device driver does not support the specified function, it should pass the 
request on to its ‘parent’ device driver. 


The RqStatus pointer contains a pointer to a memory location in the application process's data space that 
receives the I/O requests completion status. The device driver must set this memory location to the value 
Pendingerr whilst the I/O request is outstanding and a completion code when the I/O request completes. 
An J/O request may complete within the strategy vector or it may complete some time in the future, 
presumably from some interrupt. 


The RqAiPtr and RqAzPtr fields contain the argument 1 and 2 parameters as passed to the IoWithwait (or 
loAsynchronous) system services. The device driver is free to specify what these parameters are (if any). 


The operating system defines a set of common function numbers used by device drivers referred to as the 
IoFuncxxx set of defines. By convention, a device driver should select from this list, particularly if some 
of the more advanced features of the I/O system are to be used, such as attached device drivers. The more 
common defines are: 


= ToFuncRead ; read from the device. 

B  [oFuneWrite ; write to the device. 

B  loFuncClose ; close device channel. 

= = IoFuneCancel ; cancel an I/O request. 

= JoFuneSet ; set driver characteristics 

B® —IoFuncSense ; sense driver characteristics. 
® = loFuncFlush ; flush any buffers. 


The PLIB library functions p_read, p_write and p close will call the device driver with the I oFuncRead, 
loFuneWrite and IoFuncClose function numbers. Thus, if the device driver choses an alternative function 
number set, an application will not be able to use the supplied library functions. 


All resident device drivers obey the following conventions: 
= A cancel request will cancel any outstanding requests. A cancel request will not return any error. 


= A close request will ensure that any outstanding requests are completed before closing the 
channel. A close request will not return any error. 


= Only one request of a particular type can be outstanding at any one time. If a second request is 
made the device driver will panic the calling application. 


105 


ADDITIONAL SYSTEM INFORMATION 


Any functions that the strategy function does not support should be passed on to the next driver down the 
driver hierarchy. If the driver is a root driver (attached driver), this is achieved using the IoRoot 
(IoSuper)system service. If the requested function is not supported by any driver, the operating system 
will return a NotSupported error. 


When called, the DS and ES segment registers point to the data segment of the application process 
making the I/O function request. The application should ensure that the DS and ES segment registers do 
in fact point to its data segment. The device driver must obey the normal rules concerning segment 
register manipulation. The DS and ES segment registers can be reloaded if required from the IntEnt 
structure pointed at by the BP register. 


All operating system services may be called. 


PASSED 

BX contains the allocated channel control block. 
DX contains the device handle of the device driver. 
SI is a pointer to the RqEnt structure. 


BP is a pointer to the IntEnt structure. 


RETURN 


If the function request is successful, the strategy vector should return with carry clear. A request 
typically causes some I/O. If the I/O is completed by the strategy vector (eg the close function), the 
completion status should be written back to the RqStatusPtr location and the I/O semaphore signalled 
(using the IoSignal system service). If the request has not yet completed, the RqStatusPtr location should 
contain the value PendingErr and the I/O semaphore should not be signalled. 


If the function request failed the strategy vector should return with carry set and the error code in AL. 
Typically no I/O requests will be completed. 


PANIC 


The strategy vector can panic; it will cause the process making the J/O request to terminate. In most cases 
it is usual to return an error to the calling process. A major exception to this is if the calling process 
makes an I/O request of the same type as one that is currently outstanding and the device driver only 
supports one I/O request of a particular type at a time; by convention the device driver should panic the 
calling process with the PanicloPending panic code. 

PRESERVE 


The DS, ES, SS, SP and BP registers must be preserved by the strategy vector. 


a eee re 
Loadable Physical Device Driver Structure 
A loadable PDD must obey the following rules: 

«® There must be a single code segment and no data segments. 

= The code segment must start with a LibEnt structure. 


® There must be at least two supported functions, with typically a further two defined. 


Single Code Segment 


A PDD must be written to contain any internal variables within its own code segment. Typically, these 
variables are only concerned with unit (i.e. channel) allocation and hardware state. 


Data space for a particular open channel can be allocated in the heap space of the process that opens the 
device. This data space will however disappear if the process terminates, thus any variables required for 
‘freeing’ the hardware after a process terminates must exist in the code space of the device driver. 


The LibEnt Structure 
A Libent structure has the following format: 


106 


8 WRITING DEVICE DRIVERS 


2 A two byte signature 
= An eight byte name 
=» A two byte vector count 
® A vector table 
The two byte signature should contain the 'PDDSignature’ define. 


The eight byte name contains a zero terminated name, being that of the device driver. Note that there is 
no trailing colon. 


The two byte vector count contains the number of vectors that follow immediately after the count. There 
should be at least two. 


For example: 
dw PDDSignature z Its an PDD driver 
db ‘DVR.HW1',0 ; Name of the driver 
dw (VectorEnd-Vector )/2 ; Number of vectors 
Vector 
dw Dvrinstall 3; Install vector 
dw DvrRemove ; Remove vector 
VectorEnd: 


Most PDDs also define a further two vectors: 


dw DvrOpen 
dw OvrStrategy 


Open Vector 
Strategy vector 


=e 


me 


The table of vectors is a table of offsets within the device drivers code segment of the routines that 
implement the required functionality. The vector table must have the entries in the order shown in the 
example. 
Mandatory PDD functions 
All PDDs must support the following two functions: 

B DevFuncInstal LPDD called on device installation. 

® DevFuncRemovePDD called on device removal. 
Most PDDs will support the following two additional functions: 

® DevFuncOpenPDD called to open a PDD 

®  DevFuncStrategyPDD called to provide PDD functionality 


All of the routines pointed at by the function vector table will be called FAR by the operating system and 
should consequently use a FAR return machine code instruction to return back to the operating system. 


Since the FAR return address is to the operating system, it does not matter if the operating system moves 
memory whilst code in the LDD is being executed; the operating system cannot move. 


This vector will be called by the operating system when the device driver is loaded to initialise any of its 
internal variables. The install vector is called in the context of the operating system and not the process 
that is loading the device driver. 


The Devinstall operating system service will cause this vector to be called. Applications should not call 
this directly, they should use the DevLoadPpp service. 


An installable device driver may have the same name as a currently installed device driver. When 
installed, the driver is added to the end of the device driver table. When a channel to a device driver is 
being established by the operating system, it searches the device table from the end first, thus the latest 
installed device driver with the required name will be asked first for a channel. By this mechanism, 
installable device drivers can replace any of the resident drivers. 


When called, the DS and ES segment registers are in an unknown state. The device driver should take 
whatever steps necessary to obtain direct addressability to its data. For loadable device drivers this 
involves setting the DS and ES registers to the CS register. 


107 


ADDITIONAL SYSTEM INFORMATION 


The operating system will not move memory whilst in this function, thus the normal rules governing DS 
and ES may be ignored. 


All operating system services may be called except those concerning file or device access. 


PASSED 


No values are passed to the install vector. 


RETURN 
If the installation was successful, return with the carry flag clear. 


If the installation failed, return with the carry flag set and the error number in the AL register. 


PANIC 


The install vector must not panic; it will cause an operating system kernel fault if it does. 


PRESERVE 
The SS, SP and BP registers must be preserved by the install vector. 


ePD 


This vector will be called by the operating system when the device driver is requested to be unloaded. 
The remove vector is called in the context of the operating system and not the process that requests the 
unload. 


The DevRemove operating system service will cause this vector to be called. Applications should not call 
this service directly; instead, they should call the Dewelete service. 


Before the remove function is requested, the operating system will send a DevFuncHold request to all 
LDDs. The LDD is responsible for ensuring that no activity will occur during the remove. Note that any 
device driver that handles hardware interrupts must contain an LDD since only LDDs receive a hold 
request. 


If the device driver is currently busy serving a client, the remove request should return an error. 


All resident device drivers will return an error since there is no mechanism by which they can be re- 
installed. 


When called, the DS and ES segment registers are in an unknown state; the device driver should take 
whatever steps necessary to obtain direct addressability to its data. For loadable device drivers this 
involves setting the DS and ES registers to the CS register. 


The operating system will not move memory whilst in this function, thus the normal rules governing DS 
and ES may be ignored. 


All operating system services may be called except those concerning file or device access. 


PASSED 


No values are passed to the remove vector. 


RETURN 
If the remove was successful, return with the carry flag clear. 


If the remove failed, return with the carry flag set and the error number in the AL register. 


PANIC 


The remove vector must not panic; it will cause an operating system kernel fault if it does. 


PRESERVE 


The SS, SP and BP registers must be preserved by the remove vector. 


108 


8 WRITING DEVICE DRIVERS 


When an application opens a channel to an LDD, it normally uses the todpen system service. If the name 
specifies, or the LDD requires, a PDD then it needs to open a channel to a PDD. The Devopenpod system 
service will call this PDD vector to establish a channel. The LDD now has a choice of calling a PDD 
vector using the Dewvector system service or calling the fourth vector in the vector table directly. The 
fourth vector is assumed to be a strategy vector to which any parameters as required by the LDD-PDD 
interface can be passed. The FAR address of the strategy vector is returned by the DevGetPDDAddress. 
When an LDD receives a DevFuncResume it should call DevGetPpDAddress again to ensure that if the PDD 
has moved the LDD still has its correct address. 


As a design, a PDD could provide many vectors, one for each required function. The LDD would then 
use the Dewvector system service to access each of these functions. The DevGetPpDAddress will only return 
the FAR address of the fourth vector. 


When called, the DS and ES segment registers point to the data segment of the application process 
making the open function request. The application should ensure that this is indeed the case. The device 
driver must obey the normal rules concerning segment register manipulation. 


All operating system services may be called. 


PASSED 


The BX register contains a pointer to the PDD unit name. The pointer passed to the Devopenppp service is 
used to find the PDD device to open. The BX register is loaded with a pointer to the trailing colon (if 
any) in the PDD unit name. For example if the name TTY.AS5:A was passed to the DevOpenPpp service, BX 
would contain a pointer to :A upon calling the open vector. 


RETURN 
If the open was successful, return with the carry flag clear. 
If the open failed, return with the carry flag set and the error number in the AL register. 


PANIC 


The open vector can panic; it will cause the process requesting the device open to terminate. It is 
however more usual to return an error to the calling process. 


PRESERVE 
The SS, SP and BP registers must be preserved by the open vector. 


This function is defined as a convenience function for the LDD-PDD interface. 


Typically, all application function requests are routed through the strategy vector. To speed the calling 
interface, the DevGetPDDAddress operating system function will return a FAR address of this vector. 


The device driver writer defines all the functions and return values as required. 


When called, the DS and ES segment registers point to the data segment of the application process 
making the function request. The application should ensure that the DS and ES segment registers do in 
fact point to its data segment. The device driver must obey the normal rules concerning segment register 
manipulation. 


All operating system services may be called. 


PASSED 


The parameters passed are defined by the device driver write. 


RETURN 
All returns are defined by the device driver writer. 


109 


ADDITIONAL SYSTEM INFORMATION 


PANIC 


The strategy vector can panic; it will cause the process requesting the function to terminate. It is however 
more usual to return an error to the calling process. 


PRESERVE 
Which registers are preserved is defined by the device driver writer. 


110 


CHAPTER 9 


EXAMPLE DEVICE DRIVERS 


This chapter contains explanatory notes for the example device drivers supplied with the Psion C SDK. 
The source code for these examples can be found in \sibosdk\ldd. The Borland Turbo assembler is used 
throughout. 


See also the preceding Writing Device Drivers chapter and the appropriate chapters in the J/O Devices 
Reference manual. 


SaaS SS SS ea a ee eT 
An Attached Device Driver Example 


The code in atimdvr.asm contains an example of an attached device driver. Attached drivers add 
functionality to, or replace, a service provided by an underlying device driver. The example is a generic 
timeout device driver that adds a timeout facility to the underlying device driver's Pp_FREAD requests. The 
example driver may be attached to either the serial or the parallel drivers. 


The example code in ¢_atim.c shows how the device driver is loaded and attached to a serial driver. It 
also shows how the additional functionality becomes transparent to the application once the driver has 
been attached. 

The device table 


The start of the file consists of the device driver header. The name of the driver is specified to be ATM. 
The driver is an LDD type driver consisting of nine callable functions, the first eight of which are 
mandatory. The ninth function (a wait handler) is required by the device driver to function correctly. 


Note that the following notes apply specifically to the functions as used in the example driver. 


The Install Function 

The install function does not have to perform any actions apart from report that it has completed 
successfully. 

The Remove Function 


The remove function does not have to perform any actions apart from reporting that it has completed 
successfully. This makes the reasonable assumption that the user of the device driver will not attempt to 
remove it if it is associated with any open channels. 

The Hold Function 


The hold function is not required to do anything since it does not access or use hardware directly. 


The Resume Function 


Since the hold function does not do anything that needs to be undone, the resume function is not required 
to do anything (otherwise it might have performed tasks such as stopping interrupts, shutting down 
hardware etc). 

The Reset Function 


The reset function is not required to do anything: it can never be called since the driver does not ask the 
operating system to call this function if its client terminates without closing an open channel. The timer 


111 


ADDITIONAL SYSTEM INFORMATION 


device driver and the driver attached to are responsible for releasing system resources if a client 
terminates. Thus this driver can leave it up to those drivers to tidy up. 


The Units Function 


The driver can support an unlimited number of open requests subject to system memory and timer 
channel availability. 


The Open Function 


The open function is called by the operating system when an application uses the Io0pen system service to 
open the ATM: device. This is usually done via the PLIB library function p_open. 


The open function runs in the context of the process that makes the open request. Thus resource 
allocation (e.g. memory allocation requests) will be associated with that process. 


The open function allocates enough memory to hold all of the required internal variables and initialises 
the memory to zero. It then makes function nine in the device table a wait handler function by using the 
loAddHandler system service. This installs the function in the linked list of wait handler functions. The 
wait handler by default is not callable, and should, for performance reasons, only be made callable when 
it has some processing to do. 


A channel to a timer device is opened, the name TIM: is generated on the run time stack. 


The I/O system channel header is set up, the I/O system requires that a device driver has a Chanent 
structure as the first item in the allocated cell. 


Finally, if all has gone well, our driver attaches itself to the underlying driver whose already open handle 
was passed to the open function. 


If any errors occur, the device driver is responsible for tidying up all of the currently allocated resources. 


It may be noted that any I/O requests made by the device driver on its own channel (as allocated) after 
the attach request has been made will be routed to the underlying driver rather than starting from the top 
of the driver chain. Thus for example in the cancelRead procedure the lofuncCancel request will be routed 
to the strategy function of the ‘attached to’ driver and not to the strategy function of our attached driver. 


The Strategy Function 


Once opened, the strategy function will be called when an application makes an I/O request. All I/O 
requests on the open channel will be routed to our example driver which must decide how they are to be 
handled. Some I/O requests are not recognised by the example driver and should be passed to the 
underlying driver. This may be done using the loSuper system service (note that a root device driver 
would make an foRoot system service request to pass on any meaningless I/O requests). 


Some requests may be redefined; the example driver redefines the meaning of the IofuncSet and 
loFuncSense (P_FSET and P_FSENSE) Services to allow an application to set and sense the timeout values to 
use. The application that attaches the example driver to the serial driver must be careful when using these 
services, since they produce different results depending on whether the time out driver has been attached 
or not (in one case they set and sense the serial characteristics and in the other they set and sense a time 
out value). 


Some requests may be modified to enhance them; this driver enhances the IoFuncRead (P_FREAD) service 
and, as a side effect, the loFuncCancel (P_FCANCEL) service causing the read request to time out. This type 
of modification is transparent to an application. It may use the 1ofuncRead service identically regardless of 
whether this time out driver has been attached (the IoFuncRead service will of course not time out if this 
driver has not been attached). 


Because the loFuncRead service effectively runs two I/O requests, that is the lower driver's loFuncRead and 
a timer channel's toFuncRead, both requests must be cancelled by the example driver if the application 
wishes to cancel the original request. Thus the example driver is required to add functionality to the 
loFuncCancel service. 


It should be noted that an 1oFuncClose (P_FCLOSE) request should only detach itself from the lower driver 
and release any resources allocated by this drivers open function. It should not attempt to pass on the 
loFuncClose request to the lower level driver (after a detach the I/O system will no longer be able to route 
any I/O requests to a lower driver). 


The Wait Handler Function 


When an application makes an 1oFuncRead request, the example driver makes two asynchronous I/O 
requests, one on the timer and one on the lower driver. In order for the device driver to gain some 


112 


9 EXAMPLE DEVICE DRIVERS 


processing time, so as to find out what happened to these requests, it needs to enable the already installed 
wait handler routine. When the I/O semaphore is signalled, the wait handler function will be called by 
the operating system (only if the application is currently waiting for an I/O request to complete) so it can 
check to see if either of the two asynchronous requests that it made have completed. 


It is possible that neither of the outstanding requests has completed in which case the wait handler 
function should return with the carry flag clear. 


If either of the outstanding requests has completed, this driver cancels the other request using up the 
signal generated by cancelling. The wait handler should return with the carry flag set and the AL register 
set to zero since there is no more processing to do at this time. 


Although synchronous I/O requests are used within the wait handler (in cancelling and using up signals) 
the wait handler will not be called, i.e. it is not called re-entrantly by the operating system. 


SSS ae ee ae ee ee ae Sree pee pe ee ae) 
Non Interrupt Based Sound Driver 


The code in snddvr.asm contains an example of a root device driver that does not require interrupt 
service routines. 


The driver accesses the sound chip within the Series3 and can play notes passed to it from an application. 


The sound system within a Series3 can only be accessed by a single process at a time. The operating 
system has some state variables that can be used (via system services) as mutual exclusion semaphores. 


When the channel to the sound driver is opened, it requests exclusive use of the sound system. When the 
channel is closed it releases this resource. The hardware sound device is switched on only when sound is 
to be played. 


The example device driver times the duration of the notes using a system timer. This limits the device 
driver to ten notes per second as the system timer can not go beyond a resolution of one tenth of a 
second. This is not a particularly high resolution for the note duration. 


As well as the system timer, the device driver makes use of a wait handler function in order play the 
notes. 


The example code in ¢_mus.c shows how the device driver is loaded and the functions provided are used 
to generate sound. 


The device table 


The start of the file contains the device driver header. The name of the driver is specified to be mus:. The 
driver is an LDD type driver consisting of nine callable functions, the first eight of which are mandatory. 
The ninth function (nominated to be a wait handler function) is required by the device driver to function 
correctly. 


Note that the following notes apply specifically to the functions as used in the example driver. 


The Install Function 


The install function should indicate that the device driver has no channel open on it yet. This variable (in 
the device driver space) is required to know how to handle the remove, hold and resume requests. 


The Remove Function 


If the device driver currently owns the sound channel, the remove function will stop the playing of sound 
and release to the operating system the sound channel resource. 


In the normal course of events, the remove function would not be called when an application has an open 
channel to the device driver. It is however quite possible for this to occur and a device driver should 
accommodate such a possibility. The stopsound routine and HwFreeCombo operating system service should 
only be called if the driver owns the sound channel otherwise any sound and ownership from other device 
drivers (e.g. alarms) will be adversely affected when this driver is removed. 


The Hold Function 


The hold function will stop any sound that is currently being made if this driver currently owns the 
systems sound resource. This device driver does not attempt to determine how far through the current 
note (duration) it has got. When the resume function is called, the following note (if any) will be played. 


113 


ADDITIONAL SYSTEM INFORMATION 


The Resume Function 


The resume function will, if the driver currently owns the systems sound resource, simply switch back on 
the hardware sound device. No note is played at this point. Because the driver uses the services of the 
timer device driver, any outstanding timeout will eventually complete causing the device driver's wait 
handler routine to be run. This in turn determines whether or not more notes are to be played. 


The Reset Function 


Since the reset function can only be called when there is an open channel, the device driver does not have 
to check that it owns the systems sound resource. The reset function simply stops any current sound, 
powers down the sound system and marks the channel as closed. 


The Units Function 


The sound system can support no more than one user; thus the device driver supports no more than one 
open channel at any given time. Note that the device driver will report that it supports one sound channel 
whether or not that one channel is available. 


The Open Function 


The open function is called by the operating system when an application wishes to obtain a channel to a 
device called mus: (the example device driver's name). The open function attempts to obtain exclusive use 
of the sound resource by calling the HwGetCombo operating system service. It returns with the carry flag 
clear if the driver has successfully obtained the sound resource. 


The open function then allocates the I/O control block, adds function nine as a wait handler function and 
obtains a channel to a timer device. If any of these requests fail, the driver tidies up after itself. 


It finally requests that the operating system call the reset function if the client terminates without closing 
the I/O channel. Once successfully opened, the internal variable is set to indicate this. 


The Strategy Function 


The example device driver supports three functions: playing sound, cancelling the playing and closing the 
channel. This implementation uses the 1oFuncWrite (P_FWRITE) service to play sound, the loFuncCancel 
(P_FCANCEL) service to cancel playing and the IofuncClose (P_FCLOSE) service to close the channel. 


The playing of a sound is achieved by writing the user specified note at the required volume to the sound 
generation chip. The specified timeout is used to queue a timeout request on the timer channel opened by 
this device driver. When the timeout occurs, the timer device driver will write the completion status 
word and signal the I/O semaphore. Providing the application is waiting for an I/O request to complete, 
the device driver's wait handler will be called. The wait handler writes the next note into the sound chip 
thus playing the required tune. 


Here lies a fundamental difference between wait handlers and interrupt service routines. Not only does an 
application have to be waiting for an I/O request to complete but it must also be able to obtain processing 
time in which to run the wait handler code. If a higher priority process is using all of the CPU (even if 
only for a short period of time), the sound application will not get a chance to run the wait handler 
function. Thus applications using this device driver will find that notes sometimes play for a lot longer 
than originally intended. 


This effect can be observed by running the example program and switching to the system task (by 
pressing the System button on the Series3 machine for example ) forcing the system to update the lists. 


The loFuncCancel service simply cancels any outstanding write request by cancelling the outstanding 
timer request, waiting for its completion and then completing the write request with the E_FILE_CANCEL 
completion status. The wait handler is also disabled, primarily for system performance reasons. 


The 1oFuncClose request will cancel any outstanding write close the timer channel, cancel the reset 
request and release the sound resource back to the operating system. 
The Wait Handler Function 


This function should check whether the outstanding timer request has completed and, if so, start playing 
the next note. If there are no more notes to play, the original write request is completed with zero 
completion status. 


114 


9 EXAMPLE DEVICE DRIVERS 


Exercising the vectors 


The hold and resume vectors can be exercised simply by switching the machine off and then back on 
again. The sound should stop when switched off and resume when switched back on. 


The reset function can be exercised by running the example program, switching to the system task and 
terminating the example program. If the example program can be re-run and generate sound and/or the 
alarms still work then the driver has tidied up any system resources it needed to. 


a a ar a Oe ee ee ee a ea a al 
Interrupt Driven Sound Driver 


The code in sndfrc.asm contains an example of a root device driver that uses the FRC (free running 
counter) as an interrupt source to drive a sound system. 


The driver accesses the sound chip within the Series3 and has the ability to play the notes passed to it 
from an application. 


The sound system within a Series3 should only be accessed by a single process at a time. The operating 
system has some state variables that can be used (via system services) as mutual exclusion semaphores. 


Similarly the FRC should only be accessed by a single process at a time. If a second process grabs the 
FRC without the first knowing then the first will probably never receive another FRC interrupt and hence 
appear to hang. 


When the channel to the sound driver is opened, it requests exclusive use of the sound system and the 
FRC; when the channel is closed it releases these resources. It is only when some sound is to be made 
that the hardware is switched on and the notes played. 


This device driver uses the FRC as a timer to time the duration of notes. The FRC can be programmed to 
Tun at either 32Hz or 512kHz, thus allowing a much greater resolution than the system timer device 
driver that can only provide 1/10th of a second resolution. This version allows for a minimum duration 
of 10ms, allowing notes of shorter duration and hence increasing the frequency with which the interrupt 
service routine is called causing a very high percentage of the processor bandwidth to be used. 


For this driver an eight bit number is used to specify the duration of a note and hence the maximum 
duration is 2.56 seconds. 


Since the notes are changed in the interrupt service routine which is independent of most other system 
activity, a high level of accuracy of note duration can be obtained. 


The example code in ¢_musfrc.c shows how the device driver is loaded and the functions provided are 
used to generate sound. 


The device table 


The start of the file contains the device driver header. The name of the driver is specified to be mus:. The 
driver is an LDD consisting of nine callable functions, the first eight of which are mandatory. The ninth 
function (nominated to be a wait handler function) is required by the device driver in order to function 
correctly. 


Note that the following notes apply specifically to the functions as used in the example driver. 


The Install Function 


The install function should set the fact that the device driver has no channel open on it yet. This variable 
(in the device driver space) is required to know how to handle the remove, hold and resume requests. 


The Remove Function 


The remove function will, if the driver currently owns the systems sound resource, stop any sound being 
made, stop the FRC from generating interrupts, remove the FRC interrupt service routine address and 
free the sound channel. 


In the normal course of events, the remove function would not be called when an application has an open 
channel to the device driver. It is however quite possible for this to occur and a device driver should 
accommodate such a possibility. The stopSound routine and HwFreeCombo operating system service should 
only be called if the driver owns the sound channel, otherwise any sound and ownership from other 
device drivers (eg alarms) will be adversely affected when this driver is removed. 


115 


ADDITIONAL SYSTEM INFORMATION 


The Hold Function 


The hold function will, if the driver currently owns the systems sound resource, stop any sound that is 
currently being made and stop the FRC from generating interrupts. 


The device driver does not attempt to determine how far through the current note (duration) it has got; 
when the resume function is called the following note (if any) will be played. 


The Resume Function 


The resume function will, if the driver currently owns the systems sound resource, reset the FRC 
interrupt service routine address held by the operating system to point to the drivers interrupt service 
routine code. The device driver may have moved in memory and thus the absolute CS address will be 
wrong. If a write request is currently queued (i.e. a hold request was made while the driver was playing 
some sounds), the sound hardware and FRC interrupts are re-enabled. 


After a power down type hold the FRC is reset and hence needs to be re-programmed to generate 
interrupts at the required frequency. With a memory move type hold the FRC will continue its 
countdown and, if the count reaches zero, generate the next interrupt. This will be ignored by this device 
driver since the FRC is re-programmed in the reset function. 


The Reset Function 


Since the reset function can only be called when there is an open channel the device driver does not have 
to check that it owns the systems sound resource. The reset function stops any current sound, powers 
down the sound system, disables FRC interrupts, resets the FRC interrupt address back to the default 
address and marks the channel as closed. 


The Units Function 


The sound system will support no more than one user and hence the device driver supports only one open 
channel at any given time. Note that the device driver does not guarantee that a channel is available by 
reporting that it supports one channel; another system component may own the sound resource when the 
open request is made. 


The Open Function 


The open function is called by the operating system when an application wishes to obtain a channel to a 
device called mus: (our device driver name). The open function attempts to obtain exclusive use of the 
sound resource by calling the KwGetCombo operating system service. If this returns with the carry flag clear 
then the driver has successfully obtained the sound resource. 


The open function then attempts to obtain exclusive use of the FRC resource by calling HwGetChannel with 
the interrupt channel number as the hardware resource required. If this returns with the carry flag clear 
then the driver has successfully obtained the FRC resource. 


The open function then allocates the I/O control block, adds function nine as a wait handler function, and 
requests that the operating system call the reset function if the client terminates without closing the I/O 
channel. 


Once successfully opened, the Soundchan.Pid internal variable is set to the process id of the client process 
(required by the interrupt service routine) and the soundChan.OpenFrcChan is set to indicate the channel has 
been opened (as required by the hold, resume and remove vectors). 


The Strategy Function 


This device driver supports three functions: playing sound, cancelling the playing and closing the 
channel. This implementation chose to use the IoFuncwrite (P_FWRITE) service to mean play sound, the 
loFuneCancel (P_FCANCEL) service to cancel playing and the 1oFuncClose (P_FCLOSE) service to close the 
channel. 


The playing of a sound is achieved by writing the user specified note at the required volume to the sound 
generation chip. The FRC interrupt service routine is the routine that actually writes the data to the sound 
chip. As it is an interrupt service routine, it cannot gain addressability to its client data space. Thus the 
sound data to be played must be copied into the device driver space. This has a side effect that the 
maximum number of notes that can be played (by this driver) is limited by the size of the internal buffer 
as defined by the driver. The buffer can be made any size although in the majority of cases a larger buffer 
will most probably waste space. 


The device driver could be written to handle arbitrarily long sequences of notes. When the interrupt 
service routine reaches a ‘low water mark’ of notes to play, it can signal the wait handler routine which 


116 


9 EXAMPLE DEVICE DRIVERS 


will eventually run to copy more data from the client process space into the internal buffer. This is, 
however, a fairly complex task. 


When all the notes in the device driver's internal buffer have been played, the interrupt service routine 
will cali the 1oSignalByPidNoReSched system service to signal the client process that the playing of sound 
has completed. 


The wait handler function will pick up the fact that the interrupt service routine has finished (from the 
SoundChan.WriteStat variable) and report the completion status to the client; the wait handler runs in the 
context of the client process and can thus write back the completion status word. 


The loFuncCancel service simply cancels any outstanding write request by stopping any more FRC 
interrupts, stopping the sound generation, switching off the hardware and then completing the write 
request with the E_FILE_CANCEL completion status. The wait handler is also disabled, primarily for system 
performance reasons. 


The loFuneClose request will cancel any outstanding write, cancel the reset request and release the sound 
and FRC resources back to the operating system. 
The Wait Handler Function 


This function should check whether all the notes have been played in which case it has completed the 
users request. 


117 


CHAPTER 10 


WORD FILE FORMAT CONVERSION DYLS 


This chapter describes the creation of file format conversion DYLs that may be used with the Series 3/3a 
Word application. If suitable file conversion DYLs are placed in a \wdr subdirectory of the root of any 
local drive they will be detected by the Word application and used to offer additional options in the 'File 
type’ list of both the 'Open' and ‘Save as’ dialogs. 


The associated software can be installed into a \sibosdk\fconv directory from the Optional disk. 


Two file conversion DYLs are required for each file format: one to read (load) the file and one to write 
(save) it. A loader file conversion DYL must have a name of the form wifxxx.dyl and a saver file 
conversion DYL must have a name of the form ws$xxx.dyl. In both cases the characters given as xxx 
identify the nature of the file conversion. This part of the DYL name may be from three to five 
characters in length and is presented as the corresponding entry in the ‘File type' list. Thus, for example, 
the Microsoft Rich Text Format (RTF) conversion dialogs that are included in the software supplied with 
the 3Link serial interface are named wi$rtf.dyl and ws$rif.dyl, and add an 'Rtf' file type option. The text 
file conversion examples that are described at the end of this chapter would add a 'Txt' option. Where 
feasible, this text should be restricted to three characters and should represent the file name extension of a 
file in the corresponding format. 


If a file conversion is selected in the 'Open' or ‘Save as’ dialogs, the Word application loads the 
appropriate DYL, creates an instance of the first class that it contains (with class number zero) and sends 
this instance a message with message function number one (in most objects this is usually some form of 
initialisation method). This method is expected not to return until the file conversion is complete. 


It is expected that the file conversion mechanism will use an active object to provide overall control, thus 
maintaining the Word application's responsiveness to other events. A standard file converter has this 
active object as the first class in its category. Word will therefore create an instance of this class and send 
it am AO_INIT message (since, for an active object, this is the method with method number one). The basic 
mechanism is illustrated in the example code later in this chapter. 


The code of the file converter interfaces with the Series 3/3a Word application via an interface library, 

s3fconv.lib, whose services are described in the following three sections. The services are classified by 
their intended usage, although there is no formal restriction as to their actual use. If necessary for some 
particular purpose, save interface services may be used in a loader file conversion and vice versa. 


Communication between the Word application and the DYL is via the magic statics DatApp1 to DatApp5 
inclusive. DatAppi, DatApp2, DatApp3 and DatApp4 are used by the interface software and must not be used 
by DYL code. pDatApp5 points to a buffer, guaranteed to be at least P_FNAMESIZE bytes in length, containing 
a file name. The file name in this buffer may be read and, if necessary, modified by DYL code. patAppé 
and DatApp7 are available for use by DYL code. 


Save interface services 


The services described in this section are mainly intended for use in a file conversion DYL that saves a 
word processor document to a file in some other format. 


__ Present file overwrite dialog 


INT DoConfirmOverwrite(VOID); 


Present a dialog to confirm the overwriting of an existing file. 


119 


ADDITIONAL SYSTEM INFORMATION 


Returns TRuE if the used confirms that the file is to be overwritten, otherwise returns FALSE. 


UINT CountTags(VOID); 


Return a count of the current document's index tags, in preparation for one or more calls to SenseTag! tem. 


UINT SenseTagItem(UINT item, PAR_STYLE *par, PHR_STYLE *phr); 


Write, to the structs pointed to by par and phr respectively, copies of the paragraph and emphasis data for 
the text associated with index tag number item. 


Returns the number of characters to which the tag refers. 


UINT CountParaStyles(VOID); 


Return a count of the total number of the paragraph styles associated with the current document. This is 
normally called in preparation for a sequence of calls to SenseParaStyleByIndex. 


UINT CountEmphStyles(VOID); 


Return a count of the total number of the emphasis styles associated with the current document. This is 
normally called in preparation for a sequence of calls to SenseEmphStyl eBy!I ndex. 


ryieB: 


PARA_STYLE *SenseParaStyleBylIndex(UINT index); 


Return a pointer to the PARA_STYLE struct containing the paragraph style data for style number index. 


It is a programming error if the value of index does not lie between zero and n-1 (inclusive) where n is 
the value returned by an earlier call to CountParaStyles. 


EMPH_STYLE *SenseEmphStyleByIndex(UINT index); 
Return a pointer to the EMPH_STYLE struct containing the emphasis style data for style number index. 


It is a programming error if the value of index does not lie between zero and n-1 (inclusive) where n is 
the value returned by an earlier call to CountEmphStyles. 


PAR_STYLE *SenseParaStyleBySC(TEXT *psc); 


Return a pointer to the PARA_STYLE struct containing the paragraph style data for the style with the two- 
character shortcode pointed to by psc. The text pointed to by psc does not need to be null terminated. A 
paragraph style with this shortcode must exist. 


EMPH_STYLE *SenseEmphStyleBySC(TEXT *psc); 


Return a pointer to the EMPH_STYLE struct containing the emphasis style data for the emphasis with the 
two-character shortcode pointed to by psc. The text pointed to by psc does not need to be null terminated. 
An emphasis style with this shortcode must exist. 


120 


10 WORD FILE FORMAT CONVERSION DYLS 


VOID ExtractText(UINT pos, TEXT *buf, UINT len); 


Copy len bytes of document text, starting at document position pos, into the buffer pointed to by buf. 


VOID SetFileErrCINT err); 


Set the Word application's file read/write error to err, for subsequent error reporting by Word. 
This would normally be used only for reporting a failure to write a converted file. 


ee a a ee Pee eee ee ee eee ee en ee 
Load interface services 


The services described in this section are mainly intended for use in a file conversion DYL that loads a 
word processor document from a file in some other format. 


VOID CloseCurrentFile(VOID); 


Close the Word application's current file. This action must be performed before new data is loaded. 


UINT SwitchToNewFile(TEXT *pname); 


Direct the Word application to open the file whose full file specification is pointed to by pname. This 
action must be performed on successful completion of the loading of a file. 


PARA_STYLE *AppendParaStyle(PARA_STYLE *pstyle); 


Append the paragraph style described by the PARA_STYLE struct pointed to by pstyle. A paragraph or 
emphasis style with the same shortcode must not exist. 


EMPH_STYLE *AppendEmphStyle(EMPH_STYLE *pstyle); 


Append the emphasis described by the EMPH_STYLE struct pointed to by pstyle. An emphasis or paragraph 
style with the same shortcode must not exist. 


VOID DoApplyParaStyleCUINT spos, UINT epos, PARA_STYLE “*par); 


Apply the paragraph style described by the PARA_STYLE struct pointed to by par to the text between the 
start and end document positions spos and epos. 


The paragraph style is applied to to entire paragraphs, from the start of the paragraph that contains the 
position spos, to the end of the paragraph containing position epos. The end of the extended range 
includes the NULL that terminates the last paragraph in the range. 


When adding text to a document, paragraph styles must be applied to each single paragraph in turn, and 
never to a range of two or more paragraphs. To ensure that application of the style does not extend to any 
following paragraph, value of epos passed to DoApplyParaStyle should not include the paragraph's 
terminating NULL. It is an absolute requirement that the range never includes the NULL that terminates the 
last paragraph of the document. 


121 


ADDITIONAL SYSTEM INFORMATION 


VOID DoApplyEmphasis(UINT spos, UINT epos, EMPH_STYLE emph); 


Apply the emphasis described by the EMPH_STYLE struct pointed to by emph (which must point to a struct 
that was previously set up by either SetDefaultStyles or AppendEmphStyle) to the text between the start and 
end document positions spos and epos. 


The specified range may extend across paragraph boundaries but, while inserting text, should preferably 
lie within a single paragraph. If the end of the range is immediately before a paragraph's terminating 
NULL, the emphasis will also be applied to the terminating NULL. 


To ensure that application of the emphasis does not extend to any following paragraph, the value of epos 
passed to DoApplyEmphStyle should not include a paragraph's terminating NULL. It is an absolute 
requirement that the range never includes the NuLL that terminates the last paragraph of the document. 


VOID SetDefaultStyles(INT npara, INT nemph); 


Delete the document content and any exisiting paragraph and emphasis styles. Create a set of standard 
paragraph styles and text emphases. 


Setting npara to a value of one to four inclusive causes from one to four standard standard paragraph 
styles to be created. These are the same as the default paragraph styles that are provided by Word when a 
new document is created: 


No. Name Shortcode 

1 Body text BT 

2 Heading A HA 

3 Heading B HB 

4 Bulleted list BL 

Thus, setting npara to three causes the three paragraph styles BT, HA and HB to be created. 


Setting nemph to a value of one to six inclusive causes from one to six standard emphasis styles to be 
created. These are the same as the default emphasis styles that are provided by Word when a new 
document is created: 


No. Name Shortcode 
1 Normal NN 
2 Underline UU 
3 Bold BB 
4 Italic Il 
5 Superscript EE 
6 Subscript ss 


Thus, setting nemph to four causes the four emphasis styles NN, UU, BB and 11 to be created. 


A special case is selected by calling setDefaultStyles with a value of -1 (in this case the value of nemph is 
ignored). This creates default styles and emphases suitable for loading a Microsoft Rich Text Format 
(RTF) file. This option is used by the RTF file conversion DYLs that are supplied with 3Link. It creates 
a single Body text paragraph style, with shortcode B1, and six emphasis styles, with names and 
shortcodes as listed above. The attributes of these styles are adjusted to be suitable defaults for the 
loading of RTF files. 


On return from this call the document content is a single empty paragraph, to which the Body text (BT) 
paragraph style and Normal (Nn) emphasis are applied. The character content is a single NULL (the 
paragraph terminator). Note that this terminator must never be deleted, and characters must never be 
inserted after it. 


122 


10 WORD FILE FORMAT CONVERSION DYLS 


VOID InsertText(UINT pos, TEXT *buf, UINT Len); 


Insert Len bytes of text, from the buffer pointed to by buf, at document position pos in the current 
document. 


The intended use is for the serial insertion of document text that has been read from the inpiut file. In no 
circumstances should the insertion position be set to be after the document's final terminating NULL. 


VOID DeleteText(UINT post, UINT pos2); 


Delete and discard the text between document positions posi and pos2 in the current document. 


The document's final terminating NULL must never be deleted. 


SSeS SS aS ee ee ee a ee ee a ey 
Common interface services 


The services described in this section are intended for use in all file conversion DYLs, regardless of 
whether they save or load files. 


VOID StartActive(VOID *hand); 


Start the active object, with handle hand, that performs a file conversion. 


The active object is first added to the application manager's active object queue with priority 
PRIORITY_ACTIVE_FILES. The Word application is set to a 'Busy' status and, on the Series 3, is marked as 
locked, so that it will not respond to Switchfiles or Shutdown messages from the System screen. 


The active object is started by sending it an AO_QUEUVE message and the application manager is sent an 
AM_START message. The call to StartActive will not return until stopActive has been called at the 
completion of the file conversion. 


VOID StopActive(VOID); 


Mark the termination of the active object file conversion, removing the ‘Busy status from the Word 
application and, on the Series 3, removing the lock so that it will respond to Switchfiles or Shutdown 
messages from the System Screen. 


Sends the application manager an am_sTop message, allowing the earlier call to startactive to return. 


Void SetBusyStatus(UINT flag); 


If #lag is TRUE, set the Word application's ‘Busy’ status, otherwise clear it. 


: _ sé printer data 
PRINTER_PARAMS *SensePrinterParams(VOID **phand); 


Return a pointer to the Word application's PRINTER_PARAMS struct. The data in this structure may be read 
or written. 


If phand is not NULL, the handle of the Word application's instance of the PRINTER class is written to 
*phand. File conversion software is free to use all the methods of the PRINTER class. 


123 


ADDITIONAL SYSTEM INFORMATION 


WDR_MODEL *SensePrinterModel (VOID); 


Return a pointer to the Word application's printer model data, contained in a WOR_MODEL struct. The data 
in this structure may be read or written. 


VOID *SenseWDR(VOID); 


Return the handle of the Word application's instance of the wor class. File conversion software is free to 
use all the methods of the wor class. 


SS SSS a a Se ae 
Example Code 


This code in the following examples provides file conversions to and from a simple plain text format. 


Save plain text 


To avoid obscuring the basic mechanisms, the nature of the conversion and the format of the saved file 
has been kept as simple as possible. The end of each text record is marked by a single carriage return 
character. 


Each record in a text file must not exceed 256 characters in length, but the example code makes no 
attempt to enforce this. It simply saves each paragraph as a single plain text record, regardless of its 
length. In order to create files that can be loaded by the following plain text loader example, the Word 
file that is saved must not contain paragraphs that exceed 256 characters in length. A more robust 
converter would break longer paragraphs into two or more records of less than 256 characters, and could 
use an empty record to mark the end of a paragraph. 


The saver DYL's category file, ws$nxt.car, is listed below: 


LIBRARY wsStxt 
EXTERNAL olib 


INCLUDE p_std.h 
INCLUDE p_object.h 
INCLUDE olib.g 
INCLUDE appman.g 


CLASS txtsave active 
NB Must be first class in the category 

€ 

REPLACE destroy 

REPLACE ao_init 

REPLACE ao_run 

REPLACE ao_abrun 

CONSTANTS 
€ 
CHAR_CR 13 
TXTSAVE_BUFFER_LEN 256 
> 

PROPERTY 
€ 
UINT ntags; total number of index tags 
UINT curtag; current tag number 
UINT pos; current position in document text 
TEXT buf {TXTSAVE_BUFFER_LEN]; buffer for extracting document text 
> 

} 


As can be seen, it only contains the single class TXTSAVE - a more complex converter may need additional 
classes. The converter's active object class must be the first class in the category file and will always 
replace the destroy, ao_init, ao_run and ao_abrun methods. It may optionally replace the ao_queue 
method, as illustrated in a later example. 


124 


10 WORD FILE FORMAT CONVERSION DYLS 


The methods of the TxTsave class are as shown in the following listing: 


/* 
TXTWRITE.C 
ies 


#include <p_std.h> 

#include <p_file.h> 
#include <ws$txt.g> 
#include "wofconv.h" 


GLREF_D VOID *DatApp5; 


LOCAL_C INT OpenFile(PR_TXTSAVE *self) 
/* forces a .TXT extension */ 

€ 

TEXT extension[6]; 

P_INFO info; 

TEXT name {P_FNAMESIZE] ; 


*(CUWORD *)&extension(0) =". '+¢'T'<<8)> 
*(CUWORD *)&extension[2] ='X'+('T'<<B); 
extension(4]=0; 
f_fparse(&extension [0] ,DatApp5 ,&name(0] , NULL); 
if (!p_finfo(&name [0] ,&info)) 

{ 

if (!DoConfirmOverwrite()) 

return(FALSE); 

> 
f_open(&sel f->active. pcb, &name [0] ,P_FUPDATE |P_FREPLACE |P_FSTREAM_TEXT); 
return(TRUE); 
> 


LOCAL_C VOID ReplaceNulls(TEXT *p,UINT len) 
/* 
Replace the end-of-paragraph nulls with CR characters 
*/ 
{ 
TEXT *pe; 


for (pe=p+len;p<pe; p++) 


€ 

if (!*p) 
*p=CHAR_CR; 

} 


> 


#pragma METHOD_CALL 


METHOD VOID txtsave_ao_init(PR_TXTSAVE *self) 
{ 
OpenFile(self); 
self->txtsave.ntags=CountTags(); 
StartActive(self); /* does not return until the conversion is complete */ 
> 


125 


ADDITIONAL SYSTEM INFORMATION 


METHOD INT txtsave_ao_run(PR_TXTSAVE *self) 
{ 
PARA_STYLE style; 
EMPH_STYLE emphasis; 
UINT taglen, txtlen; 


taglen=SenseTagI tem(sel f->txtsave.curtagt+, &style, &emphasis); 
while (taglen) 
{ /* looping here means that a record is too long to read with WLSDYL */ 
txtlen=taglen>TXTSAVE_BUFFER_LEN ? TXTSAVE_BUFFER_LEN : taglen; 
ExtractText(self->txtsave.pos,&sel f->txtsave. buf [0], txtlen); 
ReplaceNul ls(&sel f->txtsave.buf [0] , txtlen); 
f_write(sel f->active.pcb, &sel f->txtsave.buf [0] ,txtlen); 
self->txtsave.post=txtlen; 
taglen-=txtlen; 
> 
if (self->txtsave.curtag<sel f->txtsave.ntags) 
p_send2(self,O_AO QUEUE); 
else 
p_send2(self,O DESTROY); 
return(RUN_ACTIVE_USED); 
> 


METHOD VOID txtsave_ao_abrun(PR_TXTSAVE *self) 
€ 
p_close(sel f->active.peb); 
StopActive(); 
p_supersend2(self,O_AO_ABRUN); 
p_supersend2(self,O0_DESTROY); 
> 


METHOD VOID txtsave_destroy(PR_TXTSAVE *self) 
€ 
StopActive(): 
p_supersend2(sel f,O_DESTROY); 
> 


The document is scanned by use of the document's index tags, each of which marks a range of characters 
that are formatted in the same way. If the conversion format includes formatting information, the format 
of the text may be read from the emphasis and paragraph styles that are associated with each index tag 
(pointers to these are supplied by each call to SenseTag! tem). 


This example uses a synchronous write to the file in the active object's ao_run method and uses the 
ao_queue method of the AcTIve superclass. 
Load plain text 


This example opens the input file as a true text file and thus assumes that no single record contains more 
than 256 characters. Each record is considered to be a whole paragraph. 


LIBRARY wl$txt 
EXTERNAL olib 


INCLUDE p_std.h 
INCLUDE p_object.h 
INCLUDE olib.g 
INCLUDE appman.g 


126 


10 WORD FILE FORMAT CONVERSION DYLS 


CLASS txtread active 
NB Must be first class in DYL 

€ 

REPLACE destroy 

REPLACE ao_init 

REPLACE ao_queue 

REPLACE ao_run 

REPLACE ao_abrun 

CONSTANTS 
{ 
TXTREAD_BUFLEN 256 
} 

PROPERTY 
{ 
UWORD pos; current document character content offset 
UWORD lastpos; document offset to start of the previous paragraph 
TEXT eopara; NULL - the end of paragraph marker character 
TEXT dummy; 
UWORD Len; length of text in buf {J 
TEXT buf CTXTREAD_BUFLEN] ; input text buffer 
> 

> 


As in the previous example, the category file contains only the single class TXTREAD - again, a more 
complex converter may need additional classes. The converter's active object class must be the first class 
in the category file and will always replace the destroy, ao_init, ao_run and ao_abrun methods. In this 
example the ao_queue method is also replaced. 


The methods of the TXTREAD class are as shown in the following listing: 


/* 
TXTREAD.C 
*f 


#include <p_std.h> 

#include <p_file.h> 
#include <wl$txt.g> 
#include "wofconv.h" 


GLREF_D VOID *DatApp5; /* used for file conversion DYLs... */ 
#define FileName DatApp5 /* ...to point to the source file's name */ 


LOCAL_C VOID InsertBuf(PR_TXTREAD *self, TEXT *buf, UINT len) 
€ 
InsertText (sel f->txtread.pos, buf, len); 
self->txtread.post+=len; 
} 


LOCAL_C VOID ApplyPlainStyle(PR_TXTREAD *sel f) 
€ 
PARA_STYLE *para; 
EMPH_STYLE *emph; 
TEXT paracode (2); 
TEXT emphcode [2]; 


*(WORD *)&paracode[0]='B'+('T'<<8); 

para=SenseParaStyl eBySC(&paracode [0] ); 
DoApplyParaStyle(sel f->txtread. lastpos, sel f->txtread.pos, para); 
*(WORD *)&emphcode [0] ='N'+('N'<<8); 

emph=(EMPH_STYLE *)SenseEmphStyleBySC(&emphcode [0] ); 
DoApplyEmphasis(sel f->txtread. lastpos,sel f->txtread.pos,emph); 
> 


127 


ADDITIONAL SYSTEM INFORMATION 


#pragma METHOD_CALL 


METHOD VOID txtread_ao_init(PR_TXTREAD *selLf) 
/* 
Keep the supplied filename extension. 
*7 
{ 
f_open((VOID **)&sel f->active.pcb, FileName, P_FOPEN|P_FTEXT); 
CloseCurrentFile(); 
SetDefaultStyles(4,6); 
StartActive(self); 
d 


METHOD VOID txtread_ao_queue(PR_TXTREAD *self) 
€ 
self->active. isact ive=TRUE; 
sel f->txtread. len=TXTREAD_BUFLEN; 
p_ioc5(self->active.pcb,P_FREAD,&self->active.stat,&sel f->txtread.buf [0], 
&self->txtread. len); 
> 


METHOD INT txtread_ao_run(PR_TXTREAD *self) 

€ 

if (self->active.stat==E_FILE_EOF) 
€ 
ApplyPlainStyle(self); /* apply style to the final paragraph */ 
SwitchToNewFilecFileName); 
p_send2(self,O_DESTROY); 
return(RUN_ACTIVE_USED); 
> 

f_leave(sel f->active.stat); 


if (sel f->txtread.pos) 
{ 
ApplyPlainStyle(self); /* range does not include the terminating NULL */ 
InsertBuf(self,&self->txtread.eopara, 1); 
self->txtread. lastpos=sel f->txtread.pos; 
} 
InsertBuf(self ,&sel f->txtread.buf [0] ,self->txtread. len); /* add text of next paragraph */ 


p_send2(self,0_AO_QUEUE); 
return(RUN_ACTIVE_USED); 
> 


METHOD VOID txtread_ao_abrun(PR_TXTREAD *self) 
/* 
The application is shut down after reporting any error on Loading. 
No data is ever lost by doing this, since any previous file will 
already have been saved. 
ied 

{ 

StopActive(); 

p_supersend2(sel f,0_AO_ABRUN); 

p_exit (0); 

> 


METHOD VOID txtread_destroy(PR_TXTREAD *self) 
€ 
StopActive(); 
p_supersend2(sel f ,O_DESTROY); 
> 


As an alternative to the code of ttwrite.c, this example reads the file asynchronously by means of a call 
to p_ioc in the replacement ao_queue method. 


128 


10 WORD FILE FORMAT CONVERSION DYLS 


Na a EE HET Te SI 
Debugging a conversion DYL 


Although the conversion DYLs will run on both the Series 3 and the Series 3a, debugging a conversion 
DYL must be performed on a Series 3a machine. You should use the normal arrangement, with the Series 
3a set up for debugging from a PC by means of the SIBO debugger provided with the 'C' SDK.. The 
DYL must, of course, have been built for debugging, and must have been copied into a \wdr 
subdirectory of the root of any local drive on the Series 3a. 


Since the DYL is only loaded into memory just before it is used, and is unloaded immediately after its 
work is done, it is not possible to use the debugger to set a breakpoint directly in the DYL code. The 
easiest solution is to set a breakpoint at a suitable point in the Word application and step into the DYL 
code. Once the debugger is displaying the code of the DYL, you can then debug it in the normal way, 
setting any further breakpoints that you need. 


The Series 3a's Word application is in the machine's ROM, so it is not possible to set a breakpoint in it. 
For this reason, a copy of the Word application is provided. Copy the supplied word.app into the 
m:\app\ directory of the Series 3a. From the System screen's App menu, use the Remove option to 
remove the built-in Word application and then use the Install option to install the copy from Internal. 
Then run this Word application. 


From the PC, start up a remote debugging session and break into the running Word application, using the 
Break into option of the Debugger's Process menu. Then set a breakpoint at 0x30a3 and apply the break 
point by selecting the Apply BP option from the Debugger's Process menu. 


At this point you can cause a Word conversion DYL to run by selecting either Save as or Open file from 
Word's File menu. In the resulting dialog, select the drive and file as normal, but select the File type to 
be the one that will use the appropriate DYL. For example, to debug ws$nxt.dyl, use the Save as option 
and select a File type of Txt. Then press Enter to exit the dialog. 


The Word application will hit the breakpoint, with the window showing the line: 
WORD:30A3 €81724 CALL 54BD 

Step into this call, and then step to the following Lib€nter call, when the window will show the line: 
WORD:54DF CDD2 LibEnter 

Step into this call, when the window will show the line: 
WORD:54FF  CDCF = LibSend 


Stepping into this LibSend call will bring you to the first executable line of the DYL code, at the top of an 
ao_init method. If, for example, you are debugging ws$nt.dyl, you will be at the start of the 
txtsave_ao_init method function. 


From this point the DYL code can be debugged as normal. 


129 


7 h: 
on : 7 a Pas 
Aion epee te Gree . 

= SS a ee SSeS vr - ae = ® 


ppmmscecveraess 


‘ dere hate fab 
moa Poin Ae bp Seems 2 


«Mm Sra, i a tral ye ag 
7 Rotts ihe hack, “ioe rey ity 4 


dea sri u of a? 
Fatt de nye m we'd a ge rere feat on 7 o > ueG t * lay 
> aa. | we pria oy uhm 


> Je ee aa) eT ae me igo ii tre wd tlie Bier i 
5 “er Sea 
or Letre are atl? parse 
i ae el eo Newil Hs 4 
rl BEA) 


eters 2a 9 bat gf LM tg, ged kat meth: AP poe . ue 
ion she 4 Goma qurnen J tee a? am. lp wt a8) omelt © 
: me 40 ans tena of Ynud wa accel * yom ~ 


weg) P40 © > ere ers wae! ue area Wt vrs (uty sith? 

= Po de in aw oil green Orel lene vw i i oon pe Fe 

wap.) @ @ a? Wu Pee! atehin onan | ie i 1M ashi aru nal ol 
het = - =~ wnejeedY Yas ewe aA we Tarai = 9% 


nd ~ ee : @ s te + ‘ 


=’ ‘> OP aa «@ sees te an Pe pe ow Me oil ap 


Rene" 2* em ee 
sg) oll eae Op fe & safe fy Sli oe ey 

mi; 9) * ed 
- 7. ™- = = & = | — = @ A » Ge, Cary, 4m, is 
bd Ce en = gor * 2° tates A on 


